Одной из основных проблем при использовании AJAX является невозможность навигации при помощи кнопок BACK и FORWARD, а также функций history.back(), history.go() и др., потому что изменения в веб странице, которые осуществляются через AJAX никак не отображаются на объекте History. Кроме того, страницы, полученные с использованием AJAX, невозможно добавлять в закладки.
Эти проблемы можно решить при помощи утилиты Browser History Manager, входящей в состав библиотеки YUI. Она позволяет создавать веб приложения с полностью функциональным объектом History, а также возможностью сохранять другие параметры состояния приложения, например, информацию о том, какие панели или кнопки на странице активны.
У этой утилиты есть и несколько ограничений. Основное – это невозможность использовать её в браузере Opera, из-за того что последний не обновляет свойство location.hash при использовании кнопок back/forward. Также, из-за того, что утилита записывает историю в фрагменты URL (часть, которая идет после знака #), есть ограничения на длину имени состояния. Например, для Internet Explorer этот лимит составляет 2083 символа (длина URL вместе с фрагментом).
Теперь о том как этим пользоваться.
Во первых, нужно подключить необходимые модули:
<script src="http://yui.yahooapis.com/2.4.1/build/yahoo/yahoo-min.js"></script> <script src="http://yui.yahooapis.com/2.4.1/build/event/event-min.js"></script> <script src="http://yui.yahooapis.com/2.4.1/build/history/history-min.js"></script>
Затем нужно добавить следующие две строки, сразу после тэга <body>:
<iframe id="yui-history-iframe" src="path_to_asset" style="position:absolute; top:0; left:0;width:1px; height:1px; visibility:hidden;"></iframe> <input id="yui-history-field" type="hidden" />
Вместо path-to-asset в аттрибуте url нужно вставить существующий url, ссылающийся на тот же домен, на котором находится ваша страница. Лучше всего здесь использовать относительные пути. Стили в тэге iframe необходимы только для того, чтобы сделать его невидимым.
Следующий шаг – регистрация модуля.
Здесь, модуль это видимый объект на странице с которым пользователь будет взаимодействовать и состояния которого нужно будет сохранять в истории браузера. Содержимое модуля изменяется, и для того, чтобы сохранять состояние этих изменений, и затем переходить по этим состояниям при помощи кнопок back/forward, нужно этот модуль зарегистрировать. Делается это функцией YAHOO.util.History.register(), которая принимает 3 параметра : идетнификатор модуля (не пустая строка, уникально идентифицирующая этот модуль), начальное состояние модуля (т.е. состояние, соответствующее первой точке в истории), и функция обработчик, которая будет вызываться всякий раз, когда состояние модуля изменится.
Начальное состояние это состояние объекта при первом открытии страницы. Суть в том, что пользователь мог ранее посещать эту страницу и сохранить ссылку на определённое состояние модуля, к примеру URL может выглядеть так: www.example.com/index.php#page=tenth_page. В этом случае необходимо открыть страницу именно в том состоянии, на которое указывает идентификатор состояния модуля. Чтобы получить это состояние используйте функцию YAHOO.util.History.getBookmarkedState(), которая принимает один параметр – идинтификатор модуля:
var bookmarkedState = YAHOO.util.History.getBookmarkedState("module_name");
В том случае если пользователь ввел ссылку без каких либо идентифкаторов, нужно показать страницу по умолчанию, т.е. передать в функцию register() идентификатор начального состояния модуля.
var initialState = bookmarkedState || "defaultState";
Последним аргументом в функцию register() должен быть обработчик события onStateChange. Этот обработчик будет вызываться каждый раз при изменении состояния модуля (например, при нажатии пользователем кнопок back/forward).
function stateChangeHandler(state){
// Обновляем интерфейс модуля в соответствии с параметром состояния - state
}
Теперь, получив все параметры, вызываем функцию YAHOO.util.History.register():
YAHOO.util.History.register("myModule", initialState, stateChangeHandler);
Использование метода onReady
Инициализация Browser History Manager происходит асинхронно, поэтому все его методы доступны только после полной инициализации менеджера. Для этого используется метод onReady(). Типичное применение для этого метода – это получение первоначального значения состояния и обновление интерфейса в соответствии с этим состоянием. Пример:
YAHOO.util.History.onReady(function () {
var сurrentState = YAHOO.util.History.getCurrentState("myModule");
// Обновление интерфейса модуля до состояния "сurrentState"
});
Теперь последняя составляющая инициализации History менеджера – вызов функции YAHOO.util.History.initialize():
try {
YAHOO.util.History.initialize("yui-history-field", "yui-history-iframe");
} catch (error){
// обработка ошибки инициализации
}
где, “yui-history-field” и “yui-history-iframe” – это id обязательных элементов, созданных нами выше.
Теперь, когда модуль инициализирован, можно использовать его, т.е. изменять его состояния в соответствии с выполенными изменениями. Делается это при помощи функции YAHOO.util.History.navigate(), в которую нужно передать 2 параметра – имя модуля и новое название состояния:
YAHOO.util.History.navigate("myModule", "newState");
Создание приложения
Теперь от теории к практике. Напишем простой пример коммуникации с сервером посредством AJAX с использованием History Browser Manager. Наше приложение будет иметь меню с тремя ссылками на три разные страницы, содержимое этих страниц будет асинхронно подгружаться в div контейнер. После нескольких AJAX запросов можно будет возвращаться взад/вперед при помощи кнопок back/forward.
Шаг 1
Создаем основной HTML документ, который будет содержать все необходимые процедуры. Добавляем туда необходимые библиотеки и обязательные тэги <iframe> и <input>. Необходимо также добавить библиотеку connection.js для осуществления запросов AJAX. Далее создаём меню со ссылками и контейнер для отображения информации, полученной от сервера. После первого шага, получаем следующий HTML документ:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<head>
<title>Демонстрация работы модуля Browser History Manager библиотеки YUI</title>
<style>
#header {
text-align: center;
background: #ccc;
border-top: 1px solid #aaa;
border-bottom: 1px solid #aaa;
}
#sidebar {
width: 240px;
float: left;
}
#sidebar li {
list-style-type: none;
}
#container {
width: 440px;
height: 400px;
padding: 8px;
border: 1px solid #aaa;
float: left;
overflow: auto;
}
</style>
<!-- Зависимости -->
<script src="http://yui.yahooapis.com/2.4.1/build/yahoo/yahoo-min.js"></script>
<script src="http://yui.yahooapis.com/2.4.1/build/event/event-min.js"></script>
<!-- Для использования ajax запросов -->
<script src="http://yui.yahooapis.com/2.2.2/build/connection/connection-min.js" type="text/javascript"></script>
<!-- Browser History Manager -->
<script src="http://yui.yahooapis.com/2.4.1/build/history/history-min.js"></script>
</head>
<body>
<!-- следующие 2 строки обязательны для объекта history -->
<iframe id="yui-history-iframe" src="yuihist1.html" style="position:absolute; top:0; left:0;width:1px; height:1px; visibility:hidden;"></iframe>
<input id="yui-history-field" type="hidden">
<!-- заголовок -->
<h2 id="header">Демонстрация работы модуля Browser History Manager библиотеки YUI</h2>
<!-- ссылки -->
<ul id="sidebar">
<li><a href="yuihist1.html" onclick="navigate(this);return false">Страница 1</a></li>
<li><a href="yuihist2.html" onclick="navigate(this);return false">Страница 2</a></li>
<li><a href="yuihist3.html" onclick="navigate(this);return false">Страница 3</a></li>
</ul>
<!-- контейнер для отображения полученных от сервера данных -->
<div id="container"></div>
</body>
</html>
Шаг 2 – инициализация.
Модулем в нашем приложении будет контейнер div. Именно изменения в этом объекте будут отслеживаться при помощи History менеджера. Назовем этот модуль именем “page”. Состояниями контейнера в нашем случае будут URL получаемых документов. Первым документом будет страница с именем yuihist1.html. Обработчиком события onStateChange будет функция ajaxCall(), которая будет выполнять запрос AJAX и, следовательно, изменять состояние нашего модуля. Обратите внимание на функцию onReady(), которая отображает первую страницу при инициализации приложения.
// Инициализация history
window.onload = function(){
try {
// определяем, какая страница должна быть открыта первой
var bookmark = YAHOO.util.History.getBookmarkedState("page");
var initialState = bookmark || "yuihist1.html";
// каждый раз при изменении состояния объекта history будет вызываться обработчик ajaxCall
YAHOO.util.History.register("page", initialState, ajaxCall);
YAHOO.util.History.onReady(function () {
var firstPage = YAHOO.util.History.getCurrentState("page");
ajaxCall(firstPage);
});
YAHOO.util.History.initialize("yui-history-field", "yui-history-iframe");
} catch (e){
// в случае ошибки инициализации, переопределяем функцию navigate,
// чтобы не использовать функции history
navigate = ajaxCall;
// открываем первую страницу
navigate('yuihist1.html');
}
}
Шаг 3 – создание функций, выполняющих AJAX запрос посредством History менеджера.
Функцей, которая будет выполнять AJAX запрос, является упомянутая выше ajaxCall(). Другая функция – navigate() будет выполнять AJAX запрос посредством History менеджера. Ключевой здесь является последняя строка, вызывающая функцию navigate(). Устанавливая новое имя состояния модуля, она вызывает событие onStateChanged, которое в свою очередь вызывает обработчик – функцию ajaxCall(), которой передается текущее состояние модуля (в нашем случае URL).
// функции осуществляющие ajax запрос
var callback = {
success : function(o){
document.getElementById('container').innerHTML = o.responseText;
},
failure : function(o){
alert(o.responseText);
}
}
function ajaxCall(sURL){
YAHOO.util.Connect.asyncRequest('GET', sURL, callback, null);
}
// функция, осуществляющая ajax запрос посредством объекта history
function navigate(elem){
// определяем url
var url = elem.getAttribute("href");
// параметр url будет передаваться обработчику (в нашем случае это функция ajaxCall)
YAHOO.util.History.navigate("page", url);
}
Посмотреть демо примера можно здесь
