Как смешать обновления страницы AJAXy с кнопкой «Назад», чтобы обновления оставались на месте, когда пользователь вернется? - PullRequest
7 голосов
/ 19 марта 2012

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

Это прекрасно работает в Firefox 11 / Chrome в Windows: даже если пользователь переходит в другое место и затем использует кнопку «Назад», он получает страницу с последними изменениями.

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

Я собрал простой тестовый пример здесь , который показывает это поведение.

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

P.S. Видимо, «рабочий» пример не работает в Chrome на OSX. Как можно обойти настойчивость браузера при возврате к устаревшей версии страницы?

Отчеты об ошибках , описывающие это поведение: Firefox


Bounty : Firefox и Chrome в Windows демонстрируют оба поведения (возвращаясь к измененному DOM в одном случае, но неизмененным в другом). Есть ли спецификация, описывающая, что браузер должен делать? Есть ошибки, поданные, чтобы изменить это так или иначе? У этой проблемы есть общее имя, которое я могу гуглить?

Я рассматриваю решение, в соответствии с которым я обновляю скрытый элемент с помощью JavaScript, а затем проверяю, есть ли обновление до сих пор. Если это так, кнопка «Назад» восстановила актуальный DOM, и больше ничего не нужно делать. Если нет, браузер восстановил устаревшую модель DOM, и я могу просто принудительно перезагрузить страницу, что неприятно. Любые комментарии по этому подходу также приветствуются.

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

Ответы [ 7 ]

5 голосов
/ 06 мая 2012

Встраивание Google Maps в страницу отключает bfcache (я буду использовать термин mozilla для отсутствия стандартного), поскольку страница карт, загруженная в <iframe>, использует прослушиватель unload.

Возможные причины, по которым страница не кэшируется для быстрой обратной навигации в Firefox, перечислены в MDN: Использование кэширования Firefox 1.5 .Ваша проблема указана как «страница верхнего уровня содержит фреймы, которые не кэшируются», что сбивает с толку, я попытаюсь прояснить это позже.(Другие браузеры, вероятно, используют аналогичную эвристику, поскольку эти правила были разработаны, чтобы избежать взлома существующего контента - см. Также этот ответ , на него есть несколько ссылок.)

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

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

Вам следует либо восстановить состояние страницы из JSили пометить страницу как не кешируемую (используя заголовок HTTP).Первое, конечно, приводит к лучшему взаимодействию с пользователем.Предложение @ Адама Гента выглядит правильно, мне придется проверить, к какой проблеме Firefox он относится.


Причина, по которой bfcache работает так:

  • Если браузер работаетобработчик onunload, затем восстанавливает страницу с помощью bfcache, страница может быть повреждена, поскольку сценарии часто удаляют прослушиватели событий в обработчике onunload (для «очистки», что на самом деле не требуется, за исключением старых версий IE)
  • если браузеры перестали запускать обработчики onunload на странице на том основании, что пользователь может вернуться на страницу и хотели бы ее кешировать, авторы будут жаловаться.
  • , если страница вЕсли iframe не может быть кэширован, восстановление кэшированной внешней страницы и перезагрузка внутренней страницы иногда могут их сломать (например, если обе страницы имеют одинаковый домен, внешняя страница может содержать ссылки на объекты во фрейме, которые не будут действительны послевнутренняя рамка перезагружается).Так что если iframe не кэшируется, то и родительская страница не является.

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

Надеюсь, это поможет.


[править] Я будууточните идею «пометить страницу как не кешируемую» выше.Чтобы кеш веб-браузера работал, а не против вас, важно помнить, что HTTP - это протокол для извлечения ресурсов.Например, HTML-страница, идентифицируемая по URL http://bbb.akshell.com/broken, является ресурсом.Когда вы обслуживаете ресурс по HTTP, вы указываете, как долго будет действительна копия ресурса в браузере (т. Е. Соответствует канонической версии ресурса на сервере).

Когда, как и в вашем тестовом примере, ресурсHTML-страница с выбранным пользователем элементом, размеченным особым образом, ресурс может измениться в любое время (каждый раз, когда пользователь меняет выбор).Это означает, что честный HTTP-ответ при обслуживании этого ресурса будет «не кешировать, может измениться в любое время».Затем браузер будет перезагружать страницу с сервера каждый раз, когда ему нужно будет загрузить страницу - правильное поведение, но за счет медлительности для пользователя.

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

Другой подход, который кажется более применимым к вашему случаю, в котором вы сохраняете ввод пользователя на сервере: разделите пользовательский интерфейс приложения и данные пользователя на разные ресурсы (например, статическую (HTTP-) кешируемую HTML-страницу с JS, загрузка пользовательских данных с отдельного не кешируемого URL). Запрос данных пользователя и обновление пользовательского интерфейса при загрузке. [/ Edit]

1 голос
/ 26 марта 2013

Этот трюк очистит bfcache для FireFox:

   window.onunload = function(){}
1 голос
/ 29 марта 2012

Если вам не нужны старые браузеры, вы можете использовать комбинацию локального хранилища и pushstate.

Использование решения @ lucian.pantelimon + pushstate + localalstorage даст вам то, что вы хотите.

<script>
window.addEventListener("popstate", function(e) {
    alert("hello"); // do my restoration from localstorage.
});
</script>

@ TimWi прав в том, что если браузер не запускает событие, то это своего рода «ручейка дерьма».

Одна вещь, которую вы, возможно, захотите исследовать, - это действительно грубый взлом набора фреймов (аля reddit / google images / style article article).

РЕДАКТИРОВАТЬ: похоже, что события pushstate не запускаются при нажатии кнопки «Назад» на удаленном сайте обратно на ваш сайт.

РЕДАКТИРОВАТЬ 2: похоже, у моего firefox v 10.0 есть проблемы с pushstate. Работает нормально в хроме.

0 голосов
/ 29 марта 2012

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

Однако я подозреваю, что они будут запускать событие domready. Если бы они этого не сделали, хуки событий jQuery, добавленные через $(function() { ... }), не были бы установлены, и если бы это было так, я, вероятно, столкнулся бы с этой ошибкой гораздо больше (потому что она была бы намного более видимой для меня как пользователь).

Поэтому я предлагаю обработать событие windowunload †; в рамках этого события подключите некоторый код к событию domready, которое проверяет, находится ли страница в измененном состоянии или в исходном неизмененном состоянии; и если он находится в неизмененном состоянии, принудительно перезагрузите страницу. (Теоретически вы можете выполнить перезагрузку в режиме ajaxy, чтобы это не повлияло на историю браузера.)

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

$(document.body).data('modified', true);

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

Вот моя попытка обработчика windowunload †:

$(window).unload†(function() {
    $(function() {
        if (!$(document.body).data('modified'))
            forceReload();
    });
});

Конечно, вам придется написать forceReload() самостоятельно. Это может быть простой location.reload‡() или вызов ajaxy.

† Я не помню точное название события или функцию jQuery, чтобы подключиться к нему.

‡ Аналогично, проверьте, назывался ли он reload или refresh или чем-то еще.

0 голосов
/ 22 марта 2012

Вы можете попытаться сохранить как можно больше информации в файлах cookie и «перестроить» страницу при необходимости.

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

Другим способом будет сохранение данных в window.name.Для плюсов, минусов и пары альтернатив для этого подхода посмотрите на вопрос 203075 .

РЕДАКТИРОВАТЬ: Может быть полезно сохранить состояния приложения и связать идентификатор с каждым из них, а затем добавить что-то вроде #stateID<state id here> к адресу.Когда пользователь нажимает кнопку «Назад», вы можете узнать, какое состояние загрузить.

IE:

Допустим, ваше государство содержит информацию selectMeItemSelected и mapInfo{latitude, longitude, zoomLevel}.

Когда пользователь впервые входит на сайт, который вы сохраняете stateID #1 в файлах cookie (или где-то еще, но в этом примере я буду использовать файлы cookie) как:

 state[1].selectMeItemSelected = 1;
 state[1].mapInfo.latitude = 45;
 state[1].mapInfo.longitude = 45;
 state[1].mapInfo.zoomLevel = 2;

После этого добавьте #stateID1 к адресу веб-сайта.

Когда пользователь выполняет действие, активирующее ваш код AJAX, вы сохраняете другое состояние в файлах cookie, меняете URL-адрес на #stateID2, а затем возобновляете выполнение своего кода AJAX.

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

Если вы генерируете stateID последовательно, вы должны также очистить состояния, которые следуют за текущим состоянием, перед добавлениемновое состояние.Таким образом вы получаете поведение кнопок «назад» и «вперед» в браузере: если вы нажмете назад из stateX , а затем перейдете по другой ссылке (может быть AJAX), у вас не будетактивная кнопка forward , активная для нажатия кнопки «назад» к stateX , и вы можете рассмотреть возможность очистки этого состояния.

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

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

Надеюсь, это поможет :)

0 голосов
/ 22 марта 2012

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

Например, ваше «Выберите меня!»ссылка № 3 приведет к http://bbb.akshell.com/broken#link3. Это не приведет к перезагрузке страницы.Таким образом, даже если страница загружается с нуля после нажатия кнопки «Назад», у вас все еще есть # link3 в URL-адресе, и вы можете использовать ее, чтобы перевести страницу в нужное вам состояние (в этом случае выведите «Select me!»).# 3).

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

PS Это решение «Я не доверяю браузеру».Если вы действительно доверяете - необходимо решение, которое работает в каждом из них, мне было бы интересно его увидеть.

0 голосов
/ 20 марта 2012

Ваша «рабочая» страница также не работает для меня - по крайней мере, не так, как вы этого хотите.

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

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

...