Деградабельний AJAX. Частина 2. Клієнт.
Близько тижня тому я почав розповідь про реалізацію AJAX в проектах таким чином, щоб дозволив уникнути проблем у користувачів, у яких відключено JavaScript в браузері. Тобто щоб вони отримували для себе цілком працюючі сторінки. Тоді я розповідав про так би мовити теоретичні засади розробки таких сервісів та вимоги до системи. Настав час переходити до практичної реалізації.
Основа для AJAX
Почнемо з початку, тобто з написання функцій, які будуть займаись відправкою запросів get та post на сервер. Я думаю, що не слід мені далеко заходити в теорію. Загальні відомості можна прочитати тут, а детальний опис створення проектів з AJAX для початківців можно читати поряд. Ящо буде необхідним, я повернусь до цього детальніше, а поки що в загальних рисах та коді.
function getRequest(request, url, handleResponse) {
request.onreadystatechange = function() { responseHandle(request, handleResponse); }
request.open('get', url, true);
request.send(null);
}
Наведена вище функція реалізує запит типу get сервера через переданий об’єкт request за адресою url. Коли завантаження відповді сервера буде закінчено, буде викликано функцію handleResponse (параметр функції getRequest – ім’я функції, що буде викликано). Для коректної обробки зміни стану об’єкту щапиту використовується функція responseHandle:
/* Handler for working with response */
function responseHandle(request, handler) {
if (request.readyState == 4) {
eval("responseData = (" + request.responseText + ")");
if (handler != null)
handler(responseData);
else
dsAJAXDefaultHandler(responseData);
}
}
Як можна побачити, ця функція перевіряє стан запиту, і у випадку, коли запит завершено, та отримано результат від серверу виконує перетворення отриманих від серверу даних та викликає передану в середину функцію обробки (handler), чи то функцію обробки за замовчуванням.
Дозволю звернути собі увагу на перетворення данних. Я вірішив, що ліпшим варіантом для передачі аднних з серверу буде JSON, в наслідок наявності в тому ж php функції для швидкого перетворення данних у відповідний формат json_encode, що дозволяє перетворити дані, що зберігаються у вигляді масиву, до формату “серіалізвоних” JavaScript об’єктів. Потім отриманий текст можна легко перетворити на повноцінні об’єкти за допомогою конструкції JavaScript eval. Подальша робота з отриманими даними досить проста – звичайнісінький обєкт-контенер, що зберігає в собі дані.
Яким чином писати свою функцію обробки результату? Для більшості випадків, коли за допомогою ajax замінюється частина докмента на щось інше вистачить стандартної функції обробки:
function dsAJAXDefaultHandler(responseData) {
if (ajaxElementId == null)
ajaxElementId = 'primaryContent';
document.getElementById(ajaxElementId).innerHTML = responseData.responseText;
}
Ця функція користується глобальною змінною ajaxElementId для пошуку елементу, дані в якому будуть замінені на результат запиту. Маніпулюючи значенням цієї змінної можна без жодних інших змін у коді реалізувати прості дії з аяксом.
У функції getRequest() є налог postRequest():
function postRequest(request, url, handleResponse, params) {
p = [];
if (("" + typeof(params)).toLowerCase() == "object") {
for (var k in params) {
var v = params[k];
if (v instanceof Function) {
continue;
}
p[p.length] = k + "=" +v;
}
}
params = p.join('&');
request.onreadystatechange = function() { responseHandle(request, handleResponse); }
request.open('post', url, true);
request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
request.setRequestHeader("Connection", "close");
request.send(params);
}
Ця функція виконує post запит на сервер. Залишається ще одна функція. А саме узашальнене створення запиту:
function httpRequest(reqType, url, handleResponse, params) {
var request;
if (window.XMLHttpRequest) {
request = new XMLHttpRequest();
} else if (window.ActiveXObject){
request = new ActiveXObject("Msxml2.XMLHTTP");
if (!request) {
request = new ActiveXObject("Microsoft.XMLHTTP");
}
}
if (request) {
if (reqType == 'get')
getRequest(request, pageUrl+url, handleResponse);
else if (reqType == 'post')
postRequest(request, pageUrl+url, handleResponse, params);
else
alert("Wrong request type!");
}
else
alert("Your browser does not permit the use of all of this application's features!");
}
Ця функція, виконує запит на сервер за вказаним типом, з вказаними параметрами. Підтримуються запити як для бразуерів сімейства Internet Explorer так і Firefox, Opera.
Займемось деградабельністю
Поки що я не зробив нічого нового, чи особливо ориганального. Гадаю, що десь може бути точнісінько така реалзація механізму відправки запитів на сервер та отримання і обробки відповідей сервера. Однак саме тут починаються мої “п’ять копійок”, що створюють деградабельність.
По-перше, подивимось на відправку форм. Для будь-якої форми можна для елементу типу submit додати ось таке:
onclick="javascript:sendForm(); return false;"
Такий невеликий шматочок коду змусить перед відправкою форми виконати функції sendForm, однак в результаті звичайна відправка не відбудеться, оскільки далі ми примусово повертаємо false, тобто примусово відмовляємось від подальшого виконання дій. Залишається підготувати функцію sendForm, яка вибере параметри з форми, та передасть їх на сервер через функцію htmlRequest.
Однак, якщо JavaScript не працює, то відбудеться звичане перевантаження сторінки, яке в результаті дасть те саме тільки трішки інакше.
Реалізація форм, що відправляються на сервер без перевантаження сторінки при працюючому JavaScript та звичаним перевантаженням якщо JavaScript не працює майже нічим не відрізняється від звичайної відправки форм за допомогою AJAX. Подібні реалізації ще можна знайти в Інтернеті.
А що ж роботи з посиланнями? Тут вже починається ексклюзив
. Пам’ятеєте, я минулого разу казав про зміну html за допомогою JavaSript таким чином, щоб всюди де це необхідно робота пішла через AJAX? Єдиною проблемою для цього є спосіб позначення елементів які потрібно змінити. Я для цього обрав атрибути rel та rev посилань. А саме таку схему:
1. Якщо атрибут rel приймає значення ‘ajax_ім’я_функції‘, то при натисканні на посилання відбудеться виклик функції ім’я_функції().
2. Якщо атрибут rel приймає значення ‘ajax‘, то у випадку коли атрибут rev існує та не пустий, то викликаємо запит httpRequest для типу запиту get з функцією обробки результату вказаною в атрибуті rev. Якщо атрибут rev не задано, то функцією обробки буде вважатися функція обробки данних сервера за замовчуванням (djAJAXDefaultHandler)
3. В будь-якому іншому випадку нічого не виконується.
Тепер слід навести програмний код, що виконає необхідні перетворення:
window.onload = function() {
initAjaxLinks();
}
function initAjaxLinks() {
var el = new Array();
el = document.getElementsByTagName("a"); //get all
for (var i = 0; i < el.length; i++) {
var attr = el[i].getAttribute('rel'); //get all ajax links
var outFunc = el[i].getAttribute('rev');
var re = /ajax./;
if ((attr != null) && attr.match(re)) {
el[i].setAttribute('rel', '');
var url = el[i].getAttribute('href').split('?');
if (attr != 'ajax_') {
var func = attr.split('ajax_');
el[i].setAttribute('href', 'javascript:' + func[1] + '("?' + url[1] + '");'); //escape(url[1]) ?
}
else {
if (outFunc == '' || outFunc == null)
el[i].setAttribute('href', 'javascript:dsAJAXRequest("?' + url[1] + '", null);');
else
el[i].setAttribute('href', 'javascript:dsAJAXRequest("?' + url[1] + '", '+outFunc+');');
}
}
}
}
function dsAJAXRequest(params, handler) {
httpRequest('get', params, handler);
}
В коді окрім функції модифікації посилань ще додається функція-обгортка для формування запиту на сервер.
Ще одне невеличке пояснення стосовно window.onload. Це необхідно для ініціалізації всіх посилань після завантаження даних. Тоді одразу постає питання, а що робити коли завантажатья дані через AJAX? Звичайно, що отримані дані можуть містити нові посилання, які ще необхідно проініціалізувати таким чином. В противному разі AJAX коректно спрацює лише один раз. Це також вірно і для відправки даних методом post. Ця проблема вирішується переініціалізацією писалань, тобто повторним викликом функції initAJAXLinks(). Змінимо функцію responseHadle() для автоматичного виклику цієї функції після отримання відповіді сервера:
function responseHandle(request, handler, animate) {
if (request.readyState == 4) {
eval("responseData = (" + request.responseText + ")");
error(responseData);
if (handler != null)
handler(responseData);
else
dsAJAXDefaultHandler(responseData);
initAjaxLinks();
}
}
Що буде далі?
Тепер клієнтська частина працює. Можна перевірити
Однак пока-що залишається відкритим питання, як має працювати сервер. Про це й піде мова у наступному матеріалі.
[...] собі нагадати, що деякий час тому, я описав клієнтську частину невеликої бібліотеки, що полегшує створення сайтів з використанням [...]
Pingback by Деградабельний AJAX. Частина 3. Серверна частина « GrAndSE’s blog | Серпень 9, 2008 |
[...] Муркуючи чим скористатися як клієнтською частиною, я розмірковував стосовно доцільності використання jsHttpRequest, JQuery. Однак прийшов до висновку, що мені буде достятньо функціоналу власноруч написаного JavaScript про який мова йшла не так д…. [...]
Pingback by AJAX та Joomla! « GrAndSE’s blog | Серпень 13, 2008 |