Работа с #хэшем в url + history api


Последний проект, который я делал состоит из одной страницы, и все действия выполняются через Ajax.
Конечно, понадобилось сохранять состояние страницы, и самое универсальное решение — хэш.

Если кто не в курсе, хэшем url зовется всё, что идет после символа #. Изначально это было придумано для якорей и используется до сих пор всякими способами из-за одной особенности — изменение хэша не обновляет страницу.

То есть, при клике на ссылку, которая ссылается на саму себя, но с хэшем — документ пролистывается до нужного места, но страница не перезагружается.

Более простого и универсального способа сохранения статуса Ajax приложений пока не придумано. Все браузеры работают с ним нативно, без заморочек.

Поэтому я написал простенький объект, методы которого позволили мне удобно работать с хэшем. Предлагаю его вам:
Hash = {
	// Получаем данные из адреса
	get: function() {
		var vars = {}, hash, splitter, hashes;
		if (!this.oldbrowser()) {
			var pos = window.location.href.indexOf('?');
			hashes = (pos != -1) ? decodeURIComponent(window.location.href.substr(pos + 1)) : '';
			splitter = '&';
		}
		else {
			hashes = decodeURIComponent(window.location.hash.substr(1));
			splitter = '/';
		}

		if (hashes.length == 0) {return vars;}
		else {hashes = hashes.split(splitter);}

		for (var i in hashes) {
			if (hashes.hasOwnProperty(i)) {
				hash = hashes[i].split('=');
				if (typeof hash[1] == 'undefined') {
					vars['anchor'] = hash[0];
				}
				else {
					vars[hash[0]] = hash[1];
				}
			}
		}
		return vars;
	},
	// Заменяем данные в адресе на полученный массив
	set: function(vars) {
		var hash = '';
		for (var i in vars) {
			if (vars.hasOwnProperty(i)) {
				hash += '&' + i + '=' + vars[i];
			}
		}

		if (!this.oldbrowser()) {
			if (hash.length != 0) {
				hash = '?' + hash.substr(1);
			}
			window.history.pushState(hash, '', document.location.pathname + hash);
		}
		else {
			window.location.hash = hash.substr(1);
		}
	},
	// Добавляем одно значение в адрес
	add: function(key, val) {
		var hash = this.get();
		hash[key] = val;
		this.set(hash);
	},
	// Удаляем одно значение из адреса
	remove: function(key) {
		var hash = this.get();
		delete hash[key];
		this.set(hash);
	},
	// Очищаем все значения в адресе
	clear: function() {
		this.set({});
	},
	// Проверка на поддержку history api браузером
	oldbrowser: function() {
		return !(window.history && history.pushState);
	},
};

Внимание! Объект был переписан и работает теперь через history api, по возможности. Если нет — то через хэш, как раньше.
Это означает, что в современных браузерах будут прямые адреса типа
test.ru/?var1=1&var2=2
А в IE8 и ниже
test.ru/#var1=1&var2=2

Метод Hash.oldbrowser() определяет режим работы. Вы можете перезагружать страницу при переходе на адрес с хэшем, если нужно:
if (window.location.hash != '' && Hash.oldbrowser()) {
	var uri = window.location.hash.replace('#', '?');
	window.location.href = document.location.pathname + uri;
}
Так, например, работает mSearch2 при фильтрации.

Сохраняем это в javascript файл, и можно пользоваться методами Hash.get(), Hash.set() и т.д.

Теперь пример использования. Предположим, мы хотим, чтобы при клике на ссылку с классом alert у нас происходило событие — окно с алертом:
$(document).on('click', 'a.alert', function(e) {
	alert('Событие!');
});

А теперь мы хотим дать прямую ссылку на этот алерт, чтобы любой, кто по ней перейдет увидел его, без клика:
// Обрабатываем клик по сслылке
$(document).on('click', 'a.alert', function(e) {
	Hash.add('event', 'alert');	// добавляем в хэш #event=alert
	alert('Событие!');
});
// Проверяем, что есть в хэше при загрузке страницы
$(document).ready(function() {
	var hash = Hash.get();	// получаем все значения
	if (hash.anchor) {
		window.location.hash = hash.anchor; // сохраняем родной функционал якорей
	}
	else if (hash.event == 'alert') {	// если есть событие alert - как бы кликаем по нужной ссылке
		$('a.alert').trigger('click');
	}
})

Вот так просто. Код для сохранения статуса табов:
// Обработка табов
$(document).on('click', '.nav a[data-toggle="tab"]', function (e) {
	e.preventDefault();
	var href = $(this).attr('href').substr(1);
	Hash.add('tab', href);
});
// Переключение таба при загрузке страницы
$(document).ready(function() {
	var hash = Hash.get();
	if (hash.tab) {
		$('.nav a[href="#' + hash.tab + '"]').trigger('click');
	}
})
31 декабря 2012, 12:26    Василий Наумкин   G+  
7    11571 0

Комментарии (14)

  1. Александр Балагуров 31 декабря 2012, 13:34 # 0
    Все это фигня. Захешенные страницы нужно отдавать по другому спец урлу для индексации поисковиками, а это лишний гемор. В итоге я свой аналогичный проект переписал на history.js
    1. Василий Наумкин 31 декабря 2012, 14:03 # 0
      Речь про состояние переключенных табов, всплывших окошек, или кнопок фильтров.

      Причем тут индексация?
    2. Виталий Воропаев 31 декабря 2012, 14:52 # 0
      С точки зрения организации кода, все четко. Аккуратно и ничего лишнего.
      1. Denys Butenko 02 января 2013, 17:07 # 0
        Если с главной страницы нажать кнопку «Читать дальше», страница открывается с хэшем #cut и на этой странице справа менять вкладки «Комметарии» и «Публикации» в адресной строке появляется это:
        http://modx.pro/development/520/#cut=undefined/tab=tickets
        1. Василий Наумкин 02 января 2013, 18:50 # 0
          Принято, спасибо.

          Подумаю на досуге, как разделить хэш для собственных нужд и настоящие якоря.
          1. Василий Наумкин 03 января 2013, 11:05 # 0
            Дописал обработку якорей.

            Теперь, если что, они сохраняются в #anchor=имяякоря. Если при загрузке есть такое значение в хэше — то оно перекрывает остальные и хэш меняется на якорь.

            Пытался подружить это дело с scrollTo.js, чтобы перекручивало на якорь и срабатывали остальные параметры — но глюки не смог одолеть. Поэтому теперь так: реальный якорь важнее остального.

            Проверяем — bezumkin.ru/sections/components/516/#anchor=cut/tab=tickets
          2. Влад Козьяков 02 апреля 2013, 07:50 # 0
            Василий, у вас небольшая ошибка в тексте сообщения:

            после вставки первого кода
            Сохраняем это в javascript файл, и можно пользоваться методами Hah.get(), Hash.set() и т.д.
            букву s пропустили)

            п.с.
            Много полезного у вас на ресурсе, спасибо за множество интересной информации =)
            1. Василий Наумкин 02 апреля 2013, 08:25 # 0
              Поправил, спасибо!
            2. Сергей Шлоков 07 мая 2013, 19:06 # 0
              Нужная вещь. Очень обрадовался. Взял для табов. Но как у тебя не работает. :( Мой страница с табами. Вроде все по инструкции, но не работает.
              П.С. Использую скрипт Organictab. Может он виноват?
              1. Василий Наумкин 07 мая 2013, 19:11 # 0
                У тебя все события перехватывает скрипт табов. Смотри, как ему задать callback на переключение.

                Проверять, срабатывает событие, или нет, можно простым alert().
                1. Сергей Шлоков 07 мая 2013, 19:30 # 0
                  Понял. А у тебя что за скрипт табы обрабатывает, если не секрет?
                  1. Сергей Шлоков 07 мая 2013, 23:32 # 0
                    При полном отсутствии знаний по js допилил — работает! Извиняюсь за ламерские вопросы, я не программист.
                2. Комментарий был удален.
                  1. DeDMazday 08 ноября 2015, 14:55 # 0
                    К сожалению я совсем не программист, и представленный код понимаю в лучшем случае на треть, и пользователь я не modx, а joomla, хотя для этого кода вроде бы всё равно должно быть. Попробовал прикрутить на страницу — не работает, ошибок не пишет, но и не работает, к сожалению понять что именно не пашет не разобраться, может гляните? Код в принципе интересный, и хотелось бы его попользовать.
                    ЗЫ сайт на который пытался приделать travelsbase.ru
                    Вы должны авторизоваться, чтобы оставлять комментарии.