Управление через историю

Управление историей просмотров

Управление через историю
89

Веб-программирование — HTML5 — Управление историей просмотров

История просмотров — это функциональность HTML5, которая расширяет возможности объекта history JavaScript. Звучит просто, но нужно знать, когда и зачем следует использовать эту возможность.

Если вам никогда раньше не приходилось встречаться с объектом history, не стоит волноваться по этому поводу. До сих пор он не мог предложить нам ничего полезного.

В действительности, традиционный объект history имеет только одно свойство и три основных метода. Это свойство length — содержит информацию о количестве элементов в списке истории просмотров (т.е.

в поддерживаемом браузером списке недавно посещенных веб-страниц). Вот пример использования этого свойства:

alert('У вас сохранено ' + history.length + ' страниц, в истории браузера.');

Наиболее полезным методом объекта history является метод back(). Этот метод позволяет переместить пользователя на один шаг назад в истории просмотров:

Эффект этого метода равнозначен нажатию пользователем кнопки браузера “Назад”. Подобным образом можно использовать метод forward() для перемещения на один шаг вперед или метод go() для перехода вперед или назад на определенное количество шагов.

[attention type=yellow]

Но все это не представляет большой ценности, если только вы не хотите создать для веб-страницы собственные кнопки “Назад” и “Вперед”. Но HTML5 добавляет этому объекту history несколько дополнительных возможностей, которые можно использовать для реализации намного более амбициозных задач.

[/attention]

Главной из этих возможностей является метод pushState(), который позволяет изменить URL в адресной строке браузера, не обновляя при этом содержимого страницы.

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

В такой ситуации URL страницы и ее содержимое могут рассогласоваться.

Например, если страница загрузит в динамическом режиме содержимое с другой страницы, первоначальный URL страницы не изменится, что может вызвать разнообразные проблемы с созданием закладки для этой страницы. Эту проблему можно решить с помощью отслеживания истории сеансов.

Если вы пока не видите, как это сделать не переживайте, в следующем разделе мы рассмотрим страницу, идеально подходящую для применения истории просмотров.

Проблемы с URL

В статье про объект XMLHttpRequest мы рассмотрели страницу со слайд-шоу. Щелкая по ссылкам “Пред” и “След”, посетитель может загружать и отображать на странице новое содержимое. Но самое примечательное в этом примере то, что каждое изображение загружается автономно, не вызывая перезагрузки всей страницы. Эта возможность достигается благодаря объекту XMLHttpRequest.

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

[attention type=red]

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

[/attention]

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

Эта проблема усугубляется в случае, если слайдов огромное количество, например, поток Flickr может содержать десятки и даже сотни изображений.

Традиционное решение: hashbang URL

Для решения этой проблемы некоторые веб-страницы добавляют дополнительную информацию в конец URL. Один из наиболее применяемых (и наиболее спорных) подходов, называющийся hashbang, добавляет в конец URL символы #!, за которыми следует идентифицирующий текст. Вот пример одного из таких URL:

http://mysite.com/exotic_china.html#!/Slide5

Этот метод работает потому, что браузеры рассматривают все, что идет после символа #, как фрагментную часть URL. Таким образом, в случае показанного в примере URL браузер знает, что запрашивается все та же страница exotic_china.html, но только с дополнительным фрагментом в конце.

С другой стороны, посмотрим, что произойдет, если код JavaScript изменит URL, не подставив символы #!:

http://mysite.com/exotic_china.html/Slide5

Теперь браузер отправит этот запрос веб-серверу и попытается загрузить новую страницу. Но это явно не то, что нам требуется.

Как же реализовываться метод hashbang? Сначала нам нужно изменить URL, который отображается в браузере, когда страница загружает новое изображение. Это можно сделать, присвоив значение свойству location.href с помощью кода JavaScript. Далее, по загрузке страницы нам нужно исследовать URL, извлечь из него фрагментную часть и получить с веб-сервера соответствующее динамическое содержимое.

Хотя метод hashbang получил широкое распространение, он также порождает много споров о его соответствии требованиям. Веб-разработчики начали отказываться от его использования по нескольким причинам:

  • Сложность получаемых URL. Хорошим примером будет сайт . В прошлом, после непродолжительного просмотра этого сайта URL в адресной строке браузера засорялся дополнительной информацией, порождая таких монстров, как, например, http://www..com/profile.php?id=1111111111#!/pages/Haskell/401573824771. Теперь разработчики этого сайта используют историю просмотров для поддерживающих эту возможность браузеров.
  • Отсутствие гибкости. Метод hashbang упаковывает в URL большой объем информации. Если изменить работу страницы, использующей этот метод, или ее способ сохранения информации, старые URL могут оказаться недееспособными, что вызовет крупный сбой в просмотре сайта.
  • Оптимизация поисковых систем. Поисковые системы могут рассматривать разные URL типа hashbang, практически как один и тот же URL. В случае страницы exotic-china.html это означает, что отдельные туристические достопримечательности, представляемые конкретными слайдами, не будут проиндексированы, более того, поисковые системы могут вообще игнорировать эту информацию.

Хотя веб-мастера по-разному относятся к этому методу, большинство из них разделяет мнение, что такие адреса представляют короткий этап в развитии интернета, и со временем им на смену придет возможность отслеживания истории сеансов.

HTML5-решение: история сеансов

HTML5 предоставляет другое решение проблемы с URL в отслеживании истории сеансов. Можно изменять URL любым образом, не требуя при этом добавления в него странных символов, как в случае с методом hashbang. Например, когда страница exotic_china.html загрузит пятый слайд, ее URL можно изменить так:

http://mysite.com/exotic_china5.html

В этом случае браузер не будет пытаться запрашивать страницу exotic_china5.html, а оставит прежнюю страницу, но загрузит для нее указанный слайд, а это нам и нужно. То же самое происходит, когда посетитель перемещается в обратном порядке в истории просмотра.

Например, если посетитель перейдет к следующему слайду (и URL изменится на exotic_china5.html), а потом возвратится назад к четвертому (возвращая URL к exotic_china4.

[attention type=green]

html), браузер сохраняет текущую страницу, и активирует событие, с помощью которого можно загрузить соответствующий слайд и восстановить правильную версию страницы.

[/attention]

Хотя с первого взгляда все выглядит как идеальное решение, в нем есть значительный недостаток. Чтобы эта система работала должным образом, для каждого используемого URL нужно в действительности создать страницу.

Для нашего примера это означает, что надо создать страницы exotic_china1.html, exotic_china2.html, exotic_china3.html и т.д.

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

Для крупных веб-сайтов (например, или Flickr) это не представляет большой проблемы, т.к. они могут использовать серверный код и предоставить содержимое одного и того же слайда в другой упаковке. Но самостоятельному веб-разработчику для этого может потребоваться приложить несколько больше усилий.

Теперь, когда мы понимаем, каким образом история сеансов связана со страницами, собственно использование ее не представляет никаких трудностей. История сеансов состоит всего лишь из двух методов и одного события, добавленных к объекту history.

Наиболее важным является метод pushState(), который позволяет изменить часть URL, обозначающую страницу, в любое требуемое время. По причинам безопасности остальная часть URL изменениям не поддается. (Возможность изменять основную часть URL предоставила бы мощное средство для хакеров для подделывания веб-сайтов.)

Метод pushState() принимает три аргумента, обязательным из которых является только третий — URL, выводящийся в строке адреса браузера.

Первым параметром могут быть любые данные, сохраняемые для представления текущего состояния данной страницы.

Как мы увидим далее, эти данные можно использовать, чтобы восстановить состояние страницы, если пользователь возвратится к данному URL посредством списка истории посещенных страниц браузера.

Второй параметр — это заголовок страницы, отображаемый в браузере. В настоящее время все браузеры дружно игнорируют эту подробность. Если нет надобности в установлении этих параметров, им можно просто присвоить значение null.

[attention type=yellow]

Далее приведен код, который нужно добавить в страницу exotic_china.html, чтобы изменять ее URL в соответствии с текущим отображаемым слайдом:

[/attention] function nextSlide() { if (slideNumber == 5) { slideNumber = 1; } else { slideNumber += 1; } // Добавленный код history.pushState(slideNumber, null, “exotic_china” + slideNumber + “.html”); // goToNewSlide(); return false; } function previousSlide() { if (slideNumber == 1) { slideNumber = 5; } else { slideNumber -= 1; } // Добавленный код history.pushState(slideNumber, null, “exotic_china” + slideNumber + “.html”); // goToNewSlide(); return false; }

Обратите внимание, что в качестве параметра состояния страницы используется номер текущего слайда. Важность этого обстоятельства станет понятной чуть позже, при рассмотрении события onPopState.

Результаты исполнения кода показаны на рисунке:

Используя метод pushState(), также следует иметь в виду событие onPopState, которое является его естественным дополнением. В то время как метод pushState() вставляет новый элемент в список История (History) браузера, событие onPopState предоставляет средство для обработки этого элемента, когда посетитель возвратится к нему.

Чтобы понять работу метода, рассмотрим, что происходит, когда посетитель просматривает все слайды. В процессе просмотра URL в адресной строке браузера меняется с exotic_china.html на exotic_china1.html, потом на exotic_china2.html, на exotic_china3.html и т.д. Хотя страница в действительности не изменяется, все эти URL добавляются в историю просмотра браузера.

Если пользователь щелкнет по ссылке для перехода на предыдущий слайд (например, с exotic_china3.html на exotic_china2.html), активируется событие onPopState.

Это событие предоставляет коду информацию состояния, сохраненную ранее посредством метода pushState(). Задача программиста заключается в использовании этой информации, чтобы восстановить требуемую версию страницы.

В настоящем примере это означает загрузку соответствующего слайда:

window.onpopstate = function(e) { if (e.state != null) { // Определяем номер слайда для данного состояния. // (Этот номер также можно было вырезать из URL, используя // свойство location.href, но для этого потребуется больше работы.) slideNumber = e.state; // Запрашиваем этот слайд у веб-сервера goToNewSlide(); } }

Обратите внимание, что в этом примере выполняется проверка на наличие объекта состояния, прежде чем приступать к работе. Это делается из-за того, что некоторые браузеры (включая Chrome) активируют событие onPopState при начальной загрузке страницы, даже если метод pushState() еще не вызывался.

Существует еще один метод объекта history — replaceState(), но он используется не так часто. Метод replaceState() можно применять для того, чтобы заменить информацию о состоянии, которая связана с текущей страницей, не добавляя при этом ничего в список История (History).

Поддержка браузерами истории сеансов

Функциональность отслеживания истории сеансов является сравнительно новой возможностью, хотя все последние версии основных браузеров поддерживают ее, за исключением Internet Explorer:

Поддержка браузерами истории сеансов
БраузерIEFirefoxChromeSafariOperaSafari iOSAndroid
Минимальная версия48511.54.2

Проблему, возникающую вследствие отсутствия поддержки браузером истории сеансов, можно решить несколькими способами. Если ничего не делать, просто не будут выводиться составные URL. Как раз это и происходит, если загрузить рассмотренный пример в Internet Explorer — какой бы слайд мы не загрузили, URL остается неизменно exotic_china.html.

Другой подход — активировать обновление всей страницы при загрузке нового содержимого. Этот подход имеет смысл в том случае, если предоставление качественного, значащего URL более важно, чем приятная демонстрация динамически загружаемого содержимого.

Например, этот метод применяется в онлайновом хранилище кода на сайте GitHub.

Но если просматривать этот сайт через браузер, поддерживающий историю сеансов, то содержимое не только загружается динамически, это делается с применением эффекта скольжения изображений.

[attention type=red]

Самый сложный вариант — использовать историю сеансов там, где это возможно, а где невозможно, применять резервное решение в виде URL типа hashbang. (Этот метод используется на .) Недостатком данного метода является необходимость применять два разных подхода в одной и той же странице. Можно также использовать заплатку JavaScript.

[/attention]

Источник: https://professorweb.ru/my/html/html5/level8/8_3.php

Механизм

Управление через историю

В технологической платформе 8.3.11 был введен специальный механизм — «История данных».  Этот механизм видится достаточно полезным, так как предоставляет ту функциональность, которую не редко приходится реализовывать вручную. В этой статье я попробую рассказать о том, что это за механизм, для чего он нужен и как с ним работать.

Начнем с общей теоретической информации о том, что такое история данных и как она устроена.

Описание и возможности

История данных — это механизм позволяющий хранить в базе данных упорядоченные по времени версии объектов конфигурации.

Под версией понимаются данные, которые были в объекте на момент редактирования и состояние метаданных на момент редактирования.

Хранить историю можно как для всего объекта целиком, так и для отдельных реквизитов — имеется возможность тонкой настройки работы механизма для каждого реквизита, в том числе табличные части.

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

Само по себе хранение истории достаточно бесполезная функция, поэтому с историей данных можно выполнять следующие операции:

  • записывать версию данных;
  • получать данные определенной версии;
  • удалять данные определенной версии;
  • получать разницу между двумя версиями одного объекта;
  • прочие полезные возможности.

На момент написания статьи (8.3.13) история данных поддерживается для следующих объектов:

  • общие реквизиты;
  • константы;
  • справочники;
  • документы;
  • бизнес-процессы;
  • задачи;
  • регистры сведений;
  • планы обмена;
  • планы счетов;
  • планы видов характеристик;
  • планы видов расчетов;
  • расширения конфигурации.

Работа с историей данных регулируется правами доступа и отражается в журнале регистрации.

Устройство механизма

История данных хранится в специальных таблицах информационной базы. Кроме самих данных в этих таблицах хранятся метаданные прежних версий объектов. Версии метаданных создаются в момент изменения этих самых метаданных у объекта и никак не связаны с изменением данных объекта.

https://www.youtube.com/watch?v=BcuiA0KauOE

Также следует помнить, что на данный момент система не отличает включение версионирования для реквизита от создания реквизита и отключение версионирования реквизита от удаления реквизита.

Создание версии объекта состоит из двух этапов. Сначала, автоматически или с помощью специального метода, фиксируется факт изменения объекта и информация об этом изменении попадает в очередь.

Перенос данных из очереди в таблицы базы выполняется методом ОбновитьИсторию(), этот метод можно выполнять асинхронно, например регламентным заданием.

[attention type=green]

Идущие подряд изменения одного объекта не «склеиваются» и фиксируются отдельно, вне зависимости от периодичности обновления истории данных.

[/attention]

Настройка версирования объектов осуществляется либо из конфигуратора либо при помощи встроенного языка.

Конфигуратор логично использовать в тех случаях, когда история данных потребуется во всех режимах работы приложения или на нее завязана какая-то прикладная логика.

Использование встроенного языка потребуется для реализации более гибкой системы, например когда пользователя потребуется самому выбирать для каких объектов хранить историю данных.

Для управления историей данных объектов в конфигураторе реализовано свойство «История данных», оно присутствует как у основных объектов (у справочников, например) так и у подчиненных — реквизиты, табличные части с их реквизитами, ресурсы регистров сведений.

Свойство «История данных»

По умолчанию свойство «История данных» установлено в значение «Использовать» у:

  • стандартных реквизитов;
  • реквизитов объектов;
  • реквизитов табличных частей;
  • измерений регистров сведений (без возможности отключения);
  • ресурсов регистров сведений.

Использование механизма

Для обращения к истории данных используется свойство глобального контекста ИсторияДанных, методы этого этого свойства будут рассмотрены ниже.

Управление использованием истории данных

Ниже приведены примеры того, как, средствами встроенного языка, можно управлять использованием истории данных. Отмечу, что значения свойства ИсторияДанных (полученные «через точку») берутся из конфигуратора и могут не соответствовать действительности, для получения актуальной информации нужно пользоваться методом ПолучитьНастройки().

&НаСервере Процедура УправлениеИспользованиемИсторииДанных() //Получения значения свойства “История данных” //установленного в конфигураторе у объекта ОбъектВключен = Метаданные.Справочники.Справочник1.ИсторияДанных; //у реквизита объекта РеквизитВключен = Метаданные.Справочники.Справочник1.Реквизиты.Найти(“Реквизит1”).ИсторияДанных; //у реквизита табличной части объекта РеквизитТЧВключен = Метаданные.Справочники.Справочник1.ТабличныеЧасти.ТабличнаяЧасть1.Реквизиты.Найти(“Реквизит1”).ИсторияДанных; //у стандартного реквизита объекта СтандартныйРеквизитВключен = Метаданные.Справочники.Справочник1.СтандартныеРеквизиты.Наименование.ИсторияДанных; //Получение информации об изменениях настроек истории данных Настройки = ИсторияДанных.ПолучитьНастройки(Метаданные.Справочники.Справочник1); //Изменение настройки истории данных средствами встроеннго языка Насторойки = Новый НастройкиИсторииДанных; //Настройка для самого объекта Насторойки.Использование = Истина; //Для реквизитов объекта Насторойки.ИспользованиеПолей.Вставить(“Реквизит1”, Ложь); Насторойки.ИспользованиеПолей.Вставить(“ТабличнаяЧасть1.Реквизит1”, Ложь); ИсторияДанных.УстановитьНастройки(Метаданные.Справочники.Справочник1, Насторойки); //Возвращаем настройки истории данных из конфигуратора ИсторияДанных.УстановитьНастройки(Метаданные.Справочники.Справочник1, Неопределено); КонецПроцедуры

Процедура УправлениеИспользованиемИсторииДанных() //Получения значения свойства “История данных” //установленного в конфигураторе у объекта ОбъектВключен = Метаданные.Справочники.Справочник1.ИсторияДанных; РеквизитВключен = Метаданные.Справочники.Справочник1.Реквизиты.Найти(“Реквизит1”).ИсторияДанных; //у реквизита табличной части объекта РеквизитТЧВключен = Метаданные.Справочники.Справочник1.ТабличныеЧасти.ТабличнаяЧасть1.Реквизиты.Найти(“Реквизит1”).ИсторияДанных; //у стандартного реквизита объекта СтандартныйРеквизитВключен = Метаданные.Справочники.Справочник1.СтандартныеРеквизиты.Наименование.ИсторияДанных; //Получение информации об изменениях настроек истории данных Настройки = ИсторияДанных.ПолучитьНастройки(Метаданные.Справочники.Справочник1); //Изменение настройки истории данных средствами встроеннго языка Насторойки = Новый НастройкиИсторииДанных; //Настройка для самого объекта Насторойки.Использование = Истина; Насторойки.ИспользованиеПолей.Вставить(“Реквизит1”, Ложь); Насторойки.ИспользованиеПолей.Вставить(“ТабличнаяЧасть1.Реквизит1”, Ложь); ИсторияДанных.УстановитьНастройки(Метаданные.Справочники.Справочник1, Насторойки); //Возвращаем настройки истории данных из конфигуратора ИсторияДанных.УстановитьНастройки(Метаданные.Справочники.Справочник1, Неопределено);

Запись версии

Обычно запись версии выполняется автоматически — в два этапа, как описано выше. Но иногда требуется перенести историю данных в системное хранилище из какого-либо другого хранилища. Для этого используется метод ЗаписатьВерсию() и в этом случае данные записываются сразу в таблицы информационной базы, без необходимости обновлять историю.

&НаСервере Процедура ЗаписьВерсииДанных() Данные = Справочники.Справочник1.НайтиПоНаименованию(“Элемент1”).ПолучитьОбъект(); ДатаСоздания = '20180101'; Пользователь = ПользователиИнформационнойБазы.НайтиПоИмени(“Администратор”); ИсторияДанных.ЗаписатьВерсию(Данные, ДатаСоздания, Пользователь.УникальныйИдентификатор, Пользователь.Имя, Пользователь.ПолноеИмя, ВидИзмененияДанных.Добавление, “Тестируем запись версий”); КонецПроцедуры

Процедура ЗаписьВерсииДанных() Данные = Справочники.Справочник1.НайтиПоНаименованию(“Элемент1”).ПолучитьОбъект(); ДатаСоздания = '20180101'; Пользователь = ПользователиИнформационнойБазы.НайтиПоИмени(“Администратор”); ИсторияДанных.ЗаписатьВерсию(Данные, ДатаСоздания, Пользователь.УникальныйИдентификатор, Пользователь.Имя, Пользователь.ПолноеИмя, ВидИзмененияДанных.Добавление, “Тестируем запись версий”);

Получение списка версий

Легко представить, что при интенсивной работе пользователей в информационной базе может храниться очень значительное количество версий объектов. Следовательно для анализа истории данных скорее всего потребуется гибкий инструмент позволяющий нужные версии. Таким инструментом выступает метод ВыбратьВерсии(). Этот метод позволяет указать критерии отбора (первый параметр — Отбор).

При заполнении этих критериев следует учитывать некоторые особенности:

  • Необходимо указать один из параметров — Метаданные или Данные, можно указать оба, но только в том случае, когда метаданные параметра Данные совпадают со значением параметра Метаданные;
  • Перед получением версий конкретного объекта рекомендуется выполнить обновление истории по этому объекту — ОбновитьИсторию(СсылкаНаОбъект);
  • Для отбора по пользователю используется идентификатор пользователя, который можно получить с помощью метода УникальныйИдентификатор() объекта ПользовательИнформационнойБазы;
  • Параметр ИзменениеЗначенийПолей позволяет отобрать только те версии в которых изменялись значения указанных полей;
  • Если на значения полей требуется наложить конкретные условия, то поможет параметр ЗначенияПолей.

Полную информацию о доступных параметрах отбора можно найти в синтаксис помощнике.

Получим список версий конкретного объекта, в которых изменялся указанный реквизит.

Источник: https://1c-programmer-blog.ru/programmirovanie/istoriya-dannyh-v-1s.html

Управление историей для пользы и развлечения

Управление через историю

Оригинал: http://diveintohtml5.info/history.html

Перевод: Влад Мержевич

Адресная строка браузера это, пожалуй, наиболее чокнутая часть пользовательского интерфейса в мире. Адреса сайтов есть на рекламных щитах, на поездах и даже на уличных граффити. В сочетании с кнопкой «Назад» — наиболее важной кнопкой в браузере — у вас есть мощный способ двигаться вперед и назад через огромное множество взаимосвязанных ресурсов называемых вебом.

API истории HTML5 представляет собой стандартизированный способ манипулировать историей браузера через скрипт. Часть этого API — навигация по истории — была доступна в предыдущих версиях HTML.

Новые части в HTML5 включают способ добавления записей в историю браузера, чтобы заметно изменить URL в адресной строке браузера (без переключения обновления страницы) и события, которые запускаются, когда эти записи удаляются из стека пользователя нажатием кнопки браузера «Назад».

Это означает, что URL в адресной строке браузера может продолжать выполнять свою работу как уникальный идентификатор для текущего ресурса, даже в приложениях нагруженными скриптами, которые не всегда выполняют полное обновление страницы.

Почему

Почему бы вам вручную не изменять адресную строку браузера? В конце концов, простая ссылка может перейти на новый URL, этот способ работал в течение 20 лет. И он будет продолжать работать таким образом. Этот API не пытается подорвать веб. Как раз наоборот.

В последние годы веб-разработчики нашли новые и увлекательные способы подрыва веба без какой-либо помощи со стороны новых стандартов.

API истории HTML5 на самом деле предназначен для того, чтобы адреса продолжали быть полезными в веб-приложениях нагруженными скриптами.

[attention type=yellow]

Возвращаясь к изначальным принципам, что теперь делает URL? Он определяет уникальный ресурс. Вы можете указать ссылку напрямую, добавить ее в закладки, поисковые системы ее индексируют, вы можете скопировать и вставить ее, отправить по почте кому-то еще, кто может нажать ссылку и в конечном итоге увидит тот же ресурс, что и вы первоначально. Все эти прекрасные качества URL сохраняются.

[/attention]

Поэтому мы хотим, чтобы уникальные ресурсы имели уникальный URL. Но в то же время браузеры всегда имели фундаментальное ограничение: если вы измените URL, даже через скрипт, это включит запрос к удаленному веб-серверу и приведет к обновлению страницы.

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

Нет способа сказать браузеру изменить URL и загрузить только половину страницы.

API истории HTML5 позволяет сделать это. Вместо запуска полного обновления страницы вы можете использовать скрипт, который, в сущности, скачивает половину страницы. Эта иллюзия довольно хитрая для воплощения и потребует некоторых усилий с вашей стороны. Вы внимательно следите?

Скажем, у вас есть две страницы, страница А и страница Б. Две страницы на 90% идентичны и только 10% содержимого страниц различается. Пользователь переходит на страницу А, затем пытается перейти к странице Б. Но вместо запуска полного обновления страницы, вы прерываете эту навигацию и совершаете следующие шаги вручную:

  1. Загружаете 10% из страницы Б, которые отличаются от страницы А (возможно с помощью XMLHttpRequest). Это потребует некоторых серверных изменения в вашем веб-приложении. Вам нужно будет написать код, который возвращает только 10% от страницы Б, отличающихся от страницы А. Это может быть скрытый URL или параметр запроса, невидимый конечному пользователю.
  2. Обмениваете измененное содержание (с использованием innerHtml или других методов DOM). Вам также может понадобиться сбросить любой обработчик событий для элемента внутри обменного содержания.
  3. Обновляете строку браузера с адресом страницы Б, используя особый метод из API истории HTML5, что я вам покажу в данный момент.

В конце этой иллюзии (если выполнена правильно) браузер получает DOM идентичный странице Б, как если бы вы перешли страницу Б напрямую. Строка браузера будет содержать URL, который идентичен странице Б, как если бы вы перешли на страницу Б напрямую.

Но в действительности вы не переходили на страницу Б и не делали полного обновления страницы. Это иллюзия.

Но поскольку «компилированная» страница выглядит так же, как страница Б и имеет тот же URL, что у страницы Б, пользователь ни за что не заметит разницы (и не оценит ваш тяжелый труд по микроуправлению этого опыта).

Как

API истории HTML5 это просто горстка методов объекта window.history плюс одно событие в объекте window. Вы можете использовать их, чтобы определить поддержку для API истории. Поддержка в настоящее время ограничивается самыми последними версиями некоторых браузеров, помещая эти методы прямо в лагерь «прогрессивного улучшения».

Поддержка истории
IEFirefoxSafariChromeOperaiPhoneAndroid
9.04.0+5.0+8.0+11.104.2.1+

Dive into dogs это простой, но не тривиальный пример использования API истории HTML5. Он демонстрирует типичный шаблон: большая статья со связанной встроенной фотогалереей.

В поддерживаемых браузерах нажатие на ссылки Next и Previous в фотогалерее будет обновлять фото в том же месте и обновлять URL в адресной строке браузера без запуска полного обновления страницы.

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

Это поднимает важный момент.

Если ваше веб-приложение потерпит неудачу в браузерах с отключенными скриптами, собака Якоба Нильсена придет к вам домой и насрет на ваш ковер.

Давайте обратимся к демо и посмотрим, как оно работает. Это соответствующий код для одной фотографии.

Next > < Previous

Fer, 1972

Ничего необычного здесь нет. Фотография это внутри , ссылки просто очередные элементы и все завернуто в . Важно, что это всего лишь обычные ссылки, которые действительно работают.

[attention type=red]

Весь код следует после скрипта проверки. Если пользователь использует неподдерживаемый браузер, наш причудливый код из API истории никогда не будет выполнен.

[/attention]

И конечно в целом всегда есть некоторые пользователи с отключенными скриптами.

Основная функция программы получить каждую из этих ссылок и передать ее функции addClicker(), которая делает фактическую работу по созданию пользовательского обработчика click.

function setupHistoryClicks() { addClicker(document.getElementById(“photonext”)); addClicker(document.getElementById(“photoprev”));}

Это функция addClicker(). Она берет элемент и добавляет обработчик click. С этим обработчиком получается интереснее.

function addClicker(link) { link.addEventListener(“click”, function(e) { swapPhoto(link.href); history.pushState(null, null, link.href); e.preventDefault(); }, false);}

Функция swapPhoto() выполняет первые два шага из трех нашей трехэтапной иллюзии. В первой половине функции swapPhoto() берется часть адреса ссылки — casey.html, adagio.html и др. — и строится URL в скрытой странице, которая содержит только код, требуемый для следующей фотографии.

function swapPhoto(href) { var req = new XMLHttpRequest(); req.open(“GET”, “http://diveintohtml5.info/examples/history/gallery/” + href.split(“/”).pop(), false); req.send(null);

Этот образец разметки возвращает http://diveintohtml5.info/examples/history/gallery/casey.html (вы можете проверить это в браузере, вставив URL напрямую).

Next > < Previous

Casey, 1984

Выглядит знакомо? Так и должно быть. Это тот же основной код, что у исходной страницы, используемый для отображения первой фотографии.

Вторая половина функции swapPhoto() выполняет второй шаг нашей трехэтапной иллюзии: вставляет этот новый загруженный код в текущую страницу. Помните, что существует оборачивающий все изображение, фотографию и подпись. Вставка кода новой фотографии это шутка, достаточно установить свойство innerHtml для в свойство responseText, которое возвращается от XMLHttpRequest.

if (req.status == 200) { document.getElementById(“gallery”).innerHTML = req.responseText; setupHistoryClicks(); return true; } return false;}

Также обратите внимание на вызов setupHistoryClicks(). Это необходимо, чтобы сбросить пользовательский обработчик событий click для новых вставленных ссылок. Установка innerHtml стирает любые следы старых ссылок и их обработчиков событий.

[attention type=green]

Теперь давайте вернемся к функции addClicker(). После успешной смены фотографии есть еще один шаг в нашей трехэтапной иллюзии: установить URL в адресной строке браузера без перезагрузки страницы.

[/attention]history.pushState(null, null, link.href);

Функция history.pushState() содержит три параметра:

  1. state может быть любой структурой данных JSON. Он передается обратно обработчику событий popstate, о котором вы узнаете чуть позже. Нам не нужно следить за state в этой демонстрации, так что я оставил его как null.
  2. title может быть любой строкой. Этот параметр в настоящее время не используется основными браузерами. Если вы хотите установить заголовок страницы, вы должны сохранить его в аргументе state и установить вручную в popstate.
  3. url может быть, ну, любым URL. Это URL, который должен отображаться в адресной строке браузера.

Вызов history.pushState немедленно изменит URL в адресной строке браузера. Так это конец иллюзии? Ну, не совсем. Нам еще нужно сказать о том, что происходит, когда пользователь нажимает важную кнопку «Назад».

Обычно, когда пользователь переходит на новую страницу (с полным обновлением страницы), браузер помещает новый URL в стек истории, загружает и отрисовывает новую страницу. Когда пользователь нажимает кнопку «Назад», браузер сдвигает одну страницу в стеке истории и перерисовывает предыдущую страницу.

Но что происходит теперь, когда вы сделали короткое замыкание этой навигации, чтобы избежать полного обновления страницы? Итак, вы поддельно «двинулись вперед» на новый URL, так что теперь необходимо также поддельно «двинуться назад» к предыдущему URL.

И ключ к поддельному «двинуться назад» в событии popstate.

window.addEventListener(“popstate”, function(e) { swapPhoto(location.pathname);}, false)

После того как вы использовали функцию history.pushState() для смещения поддельного URL в стеке истории браузера, когда пользователь нажимает кнопку «Назад», в браузере срабатывает событие popstate на объекте window. Это ваш шанс завершить иллюзию раз и навсегда. Потому что не достаточно сделать исчезновение чего-то, вы также должны вернуть его.

В этой демонстрации «вернуть его» так же просто, как смена исходной фотографии, которую мы делаем с помощью вызова swapPhoto() в текущей локации. К тому времени popstate будет вызван, URL отображается в адресной строке браузера как измененный на предыдущий URL. Кроме того, глобальное свойство location уже был обновлено с предыдущим URL.

Чтобы помочь вам представить это, давайте пройдем по шагам через всю иллюзию от начала до конца:

  • Пользователь загружает http://diveintohtml5.info/examples/history/fer.html, смотрит историю и фотографию Фер.
  • Пользователь щелкает по ссылке Next, у элемента атрибут href установлен как http://diveintohtml5.info/examples/history/casey.html.
  • Вместо перехода на http://diveintohtml5.info/examples/history/casey.htmlс полной перезагрузкой страницы, пользовательский обработчик click на элементе перехватывает щелчок и выполняет собственный код.
  • Наш собственный обработчик click вызывает функцию swapPhoto(), которая создает объект XMLHttpRequest для синхронной загрузки фрагмента HTML по адресу http://diveintohtml5.info/examples/history/gallery/casey.html.
  • Функция swapPhoto() устанавливает свойство innerHTML обертке фотогалереи (элемент ), тем самым заменив фотографию Фер на фотографию Кейси.
  • Наконец, наш обработчик click вызывает функцию history.pushState(), чтобы вручную изменить URL в адресной строке браузера на http://diveintohtml5.info/examples/history/casey.html.
  • Пользователь нажимает кнопку «Назад» браузера.
  • Браузер замечает, что URL вручную помещается в стек истории (по функции history.pushState()). Вместо того, чтобы перейти на предыдущий URL и перерисовать всю страницу, браузер просто обновит адресную строку на предыдущий URL (http://diveintohtml5.info/examples/history/fer.html) и запустит событие popstate.
  • Наш пользовательский обработчик popstate снова вызовет функцию swapPhoto(), на этот раз с предыдущим URL, что сейчас уже видно в адресной строке браузера.
  • Снова используя XMLHttpRequest, функция swapPhoto() загружает фрагмент HTML расположенный в http://diveintohtml5.info/examples/history/gallery/fer.html и устанавливает свойство innerHtml для элемента , тем самым заменяя фотографию Кейси на фотографию Фер.

Иллюзия завершена. Все видимые доказательства (содержание страницы и URL в адресной строке) убеждают пользователя, что у него был переход вперед и назад на страницу. Но полного обновления страницы не происходит — все это было тщательно выполненной иллюзией.

Что еще почитать

Источник: http://htmlbook.ru/html5/history

Как писать качественные пользовательские истории

Управление через историю

Анна Мининкова, менеджер мобильной аналитики в JetSmarter, написала колонку специально для Нетологии о том, как использовать пользовательские истории для разработки требований продукта.

Пользовательская история – это легковесный инструмент для документации пользовательских требований к разработке продукта. История описывает функциональность системы с точки зрения пользователя с определенной ролью и целью этой системы.

Как  я .

Часто команды задаются вопросом: зачем работать над документацией вообще, если можно просто создать задачи в трекере и сразу начать внедрять новый функционал?

Но основная задача любой документации — выявить и описать потребности пользователя, для которого разрабатывается продукт.

Она создана не столько, чтобы описать всё, что должен делать продукт, сколько чтобы убедиться, что команда знает, зачем пользователю продукт и как именно он будет им пользоваться.

Невнимание к этой разнице в целях очень часто приводит к тому, что продукт работает «как написано», но не приносит никакой пользовательской ценности.

И если формат традиционной нарративной функциональной спецификации позволяет легко потерять ценность за событие «по нажатию на кнопку X должно происходить событие Y», то формат пользовательской истории: «Как  я  », позволяет команде задуматься над этой ценностью на самом раннем этапе.

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

История — это результат обсуждения команды разработки и бизнес-пользователей, который фиксируется в максимально ненагруженной форме.

[attention type=yellow]

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

[/attention]

Пользовательская история включает в себя следующие элементы:

  1. Текст истории.
  2. Критерии приемки: как мы поймем, что история завершена.
  3. Тестирование. В идеальном случае пользовательские истории служат еще и легковесной тестовой документацией, в которой фиксируются тест кейсы.
  4. Технические заметки. Сюда часто попадает информация об ограничениях системы, к примеру, о необходимости поддерживать определенный формат данных.

Как писать пользовательские истории

В идеальном случае черновая пользовательская история должна быть написана внутренним или внешним бизнес-пользователем, который заказывает разработку нового продукта или функционала у команды, а после коллаборативно разбирается вместе с командой разработки.

Текст истории должен объяснять действия пользователя, его потребность и результат, на который он надеется.

Как  я .

Чтобы понять, хорошей ли получилась пользовательская история очень удобно использовать следующий чек-лист:

  • Разработка этой истории относительно независима. Чем больше программных зависимостей и ограничений, тем сложнее будет спланировать разработку такой истории.
  • Ценность. Финальная часть конструкции «я хочу что-то получить с такой-то целью» должна содержать цель, которую вся команда признает важной и осмысленной.
  • История должна быть легко оцениваема. Если она слишком большая или слишком размытая, чтобы оценить, то ее стоит разбить на меньшие части.
  • Разработка не должна занимать больше недели. Иначе ее придется дробить на составляющие.
  • Критерии приемки должны быть довольно четкими, чтобы по ним можно было протестировать продукт.

История для пользователя

Пример: «Как пользователь я хочу управлять отображением спецпредложений, чтобы удалять неактуальные и устаревшие».

Что не так с этой историей? Все важные части, кажется, на месте, но присмотревшись ближе, мы не знаем, для кого мы проектируем эту историю. Возможно, наш пользователь — администратор системы, которому нужно премодерировать показ спецпредложений от рекламодателей? Или, возможно, он рекламодатель, которому нужно управлять показом спецпредложений в списке?

У этих пользователей будут совершенно разные ожидания от системы. Ошибка этой истории — невнимание к роли пользователя в ней.

История для разработчика

Пример: «Как разработчик я хочу перейти на программную библиотеку Х, чтобы у меня была последняя версия библиотеки Х»

Часто такие истории пишутся, чтобы объяснить, что нужно сделать в рамках технического долга и рефакторинга, и здесь уже команда выступает непосредственным заказчиком. Однако, убедить бизнес в необходимости такой истории будет очень сложно, ведь на словах она не создает никакой ценности для пользователя. Как же с ней быть, если задача действительно нужная и полезная?

Необходимо посмотреть на то, что делает библиотека Х для конечного пользователя продукта. К примеру, она позволяет быстрее создавать спецпредложения и убирает задержку после их создания. Тогда история может звучать так:

«Как рекламодатель я хочу, чтобы в системе не было задержек после создания спецпредложений, чтобы я мог быстрее работать с большим объемом спецпредложений».

Перепишите ее с пользовательской точки зрения: «Как рекламодатель я хочу, чтобы система позволяла создавать мне папки, чтобы я мог быстрее работать с большими списками объявлений»

Технические заметки к этой истории могут выглядеть следующим образом:

  • «Отрефакторить механизм добавления спецпредложений».
  • «Обновить версию библиотеки, отвечающей за анимации добавления спецпредложений».

При этом к такой истории гораздо проще написать критерии приемки:

  • «Пользователь не должен видеть задержки после создания спецпредложения при нормальной скорости интернета».

Никакой бизнес-ценности для пользователя

Пример: «Как администратор системы я хочу чтобы у меня была возможность сортировать спецпредложения».

Вроде бы все на месте, кроме ценности для пользователя. Зачем администратору сортировать спецпредложения? Не понимая какая цель у сортировки, нельзя сформулировать и дальнейшие требования к ней, а история теряет смысл.

Практические советы по написанию пользовательских историй

  • Выбирайте в пользу разбивки истории на мелкие составляющие вместо одной громоздкой.
  • По возможности избегайте технического жаргона в историях — особенно если впоследствии вашим бизнес-пользователям нужно будет приоритезировать истории.
  • Избегайте обсуждения конкретных UI элементов. История должна оставаться актуальной, даже если визуальная реализация меняется.
  • Обязательно оценивайте усилия по разработке истории.
  • История должна приводить к понятному и ценному результату.

Порочные практики

Иногда бизнес-пользователи из лучших побуждений пишут огромные подробные истории. В этом случае команда часто сдается и пропускает обсуждения, видя, что все нюансы «кажутся» освещенными. Как результат, часть требований теряется, ведь один человек редко может охватить абсолютно все детали.

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

  • Перевес в пользу технических историй

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

Мнение автора и редакции может не совпадать. Хотите написать колонку для «Нетологии»? Читайте наши условия публикации.

Читать еще 

Обучение

Источник: https://netology.ru/blog/history-klient

Сам себе врач
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: