Я пытаюсь сохранить и восстановить состояние checkbox
и других элементов, используя localStorage
, когда пользователь дублирует или закрывает вкладку. Это работает хорошо, когда у меня есть только checkbox
, но как только я добавляю дополнительные элементы, состояние checkbox
не сохраняется правильно в Chrome и Edge. Вдохновленный аналогичной проблемой, обсуждавшейся здесь, я создал версию, демонстрирующую проблему:
function save() {
var checkbox = document.getElementById('checkbox1zaal1');
var textarea = document.querySelector('textarea');
localStorage.setItem('checkbox1zaal1', checkbox.checked);
localStorage.setItem('box', textarea.value);
}
function load() {
var checked = JSON.parse(localStorage.getItem('checkbox1zaal1'));
document.getElementById("checkbox1zaal1").checked = checked;
document.querySelector('textarea').value = JSON.parse(localStorage.getItem('box'));
}
function wis() {
location.reload();
localStorage.clear();
}
load();
<input type = "button" id = "ReserveerButton1" value = "save" onclick = "save()" />
<input type = "button" id = "Wisbutton1" value = "delete" onclick = "wis()" />
<input type = "checkbox" id = "checkbox1zaal1">
<textarea>Hello, world!</textarea>
Действия по воспроизведению проблемы:
Флажок на вкладке дубликатов не установлен.
Что мне не хватает? Это известная ошибка в Chrome и Edge, или есть другой подход, который я могу попробовать, чтобы гарантировать, что состояние checkbox
сохраняется правильно вместе с другими элементами?
Хорошо, а что вы уже сделали, чтобы попытаться отладить это? Вы проверили, какие значения были сохранены, какие были прочитаны?
@CBroe: Я пробовал разные элементы формы, и, похоже, их значения сохраняются и восстанавливаются без проблем. Однако когда дело доходит до checkbox
, Chrome и Edge не могут правильно отображать сохраненные данные.
Так что же сохраняется? Пройдите проверку с помощью инструментов разработчика вашего браузера и запишите значение checked
внутри load
на консоль.
@CBroe: я проверяю checkbox
, дублирую вкладку, а настоящий checkbox
на странице не проверяется, хотя в консоли у меня есть следующее: console.info(checked); // true
Это странно!
У меня это отлично работает в Chrome с приведенным выше демонстрационным URL-адресом. Не имеет значения, перезагружаю ли я текущую вкладку или открываю ее снова в новой — флажок всегда принимает предыдущее состояние save
-d.
@CBroe: Пожалуйста, попробуйте, не редактируя textarea
.
Это тоже работает; но текстовое поле выдает ошибки - потому что значение, которое вы для этого сохраняете, не может быть проанализировано как JSON. Поэтому удалите JSON.parse
для текстового поля.
@CBroe: Боюсь, нет. Он неправильно отображает сохраненное состояние checkbox
. Я использую последнюю версию Chrome для Windows и macOS. Пожалуйста, удалите данные просмотра и повторите попытку.
Даже когда я пытаюсь перейти на приватную вкладку (поэтому нет существующей истории данных просмотра) - те же результаты. Может быть, у вас какое-то расширение браузера мешает?
@CBroe: никакого расширения. Это странно! Я просто открываю демо-страницу, проверяю checkbox
, нажимаю кнопку «Сохранить» и дублирую вкладку. В новой вкладке checkbox
не отмечен.
На основе предоставленной вами демонстрации я провел тест, но не воспроизвел описанную вами проблему с флажком в Edge и Chrome. Пожалуйста, предоставьте воспроизводимую демонстрацию или расскажите мне более подробные шаги по воспроизведению. Кроме того, какая у вас версия Edge?
@kobewang: «Пожалуйста, предоставьте воспроизводимую демонстрацию или расскажите мне более подробные шаги по воспроизведению». Демо-версию можно воспроизвести: перейдите на демо-страницу. Установите флажок. Нажмите кнопку «Сохранить», а затем дублируйте вкладку. Флажок на вкладке дубликатов не установлен. Chrome: 126.0.6478.127 (официальная сборка) (64-разрядная версия), Edge: 126.0.2592.87 (официальная сборка) (64-разрядная версия)
@CBroe Может воспроизводиться в Chrome 126.0.6478.127 в Windows и OSX. Но только для начальной загрузки второй вкладки после "щелчок правой кнопкой мыши -> Дублировать". Т.е. если я обновлю вторую вкладку, значения будут установлены правильно. Я также исправил ошибки с помощью JSON.parse
, которые не повлияли на такое поведение.
@kobewang Я могу прекрасно воспроизвести, выполнив точные действия, указанные выше ... (т. е. «дублировать вкладку», что означает «щелкнуть правой кнопкой мыши по вкладке -> дублировать»)
Нет необходимости анализировать json для текстовой области. document.querySelector('textarea').value = localStorage.getItem('box');
. Это решит вашу проблему
ДОБАВЛЯТЬ :
var as = document.getElementById('textbox').value
//Code to sent to discord webhook
sent(as)
Каким должен быть sent
?
Это похоже на функцию для отправки в веб-хук Discord, я не могу это сделать, поэтому вы просто добавляете саму функцию для отправки, обработки и прочего.
Так какое отношение к этому вопросу имеет смерть?
Я выполнил шаги, которые вы предоставили, чтобы воспроизвести проблему, но это не воспроизвело ее. Но когда я отключил настройку «Разрешить сайтам сохранять и читать данные cookie (рекомендуется)», я смог воспроизвести проблему. Поэтому введите «edge://settings/content/cookies» в адресной строке Edge и проверьте, не отключен ли этот параметр, чтобы вызвать эту проблему.
Отключение этого параметра предотвращает запись Edge в локальное хранилище. Но, как подтвердил ОП, запись в локальное хранилище работает, этот параметр явно включен...
В вашем коде есть одна серьезная проблема: вы используете JSON.parse
, фактически JSON.stringify
не обрабатывая значения, которые вы пытаетесь проанализировать позже. Это приводит к ошибке при попытке проанализировать значение textarea
. Эту ошибку легко увидеть, открыв инструменты разработчика Chrome...
Поэтому я бы посоветовал исправить ваш load
, избавившись от JSON.parse
.
function load(){
var checked = localStorage.getItem('checkbox1zaal1') === "true";
document.getElementById("checkbox1zaal1").checked = checked;
document.querySelector('textarea').value = localStorage.getItem('box');
}
Но даже после исправления этой ошибки она по-прежнему не работает должным образом (по крайней мере, в браузерах на базе Chrome. Работает с Safari). Однако не знаю, почему это не удается, потому что (по крайней мере, для меня) происходит сбой только при начальной загрузке на новой вкладке. Т.е. если я сделаю «Обновить» на вновь созданной вкладке, она будет работать как положено.
Но я смогу заставить его работать, если сделаю
setTimeout(load, 0)
вместо
load();
Похоже, это какая-то проблема с синхронизацией или состояние гонки в движке браузера. Может быть, кто-то с большим пониманием сможет дать здесь объяснение. Используя setTimeout
, мы фактически больше не вызываем load
синхронно в процессе рендеринга, а только после завершения рендеринга. Кажется, здесь есть разница...
Также кажется странным, что флажок на вкладке «Дублировать» на самом деле снят, потому что, если я полностью удалю вызов load
, «Дублировать вкладку» восстановит значения входных данных, как если бы они были на вкладке источника ... Так что, возможно, это конфликт/проблема времени между восстановлением входных значений с помощью операций «Дублировать» и обновлением входных данных из сценария. Это предположение также может объяснить, почему все работает так, как ожидалось, если выполнить «Обновить» на дублированной вкладке. И что еще более странно, это также работает, если textarea
не пуст при сохранении или когда я полностью удаляю textarea
и весь связанный с ним код...
Так что, вероятно, это не тот канонический ответ, который вы ищете. На мой взгляд, это больше похоже на ошибку в Chrome (и производных браузерах, таких как Edge или Brave). Тем более, что он работает так, как и ожидалось, в браузерах, отличных от Chrome, таких как Safari или Firefox, а также работает в браузерах на базе Chrome, когда вы обновляете или просто вставляете URL-адрес в новую пустую вкладку. Возможно, это даже зависит от версии и/или версии ОС, потому что она вполне работает и для других пользователей в Chrome. Вы можете попробовать сообщить о проблеме в Chromium (но я предлагаю сначала исправить ошибки с помощью JSON.parse
)
Ваш код отлично работает с последней версией Chrome на MacOS, конечно, если скрипт размещен после других элементов HTML (после textarea
) или, по крайней мере, первоначальный вызов load()
выполняется на DOMContentLoaded
. На какой ОС вы тестируете?
@kikon Я могу воспроизвести проблему с Chrome 126.0.6478.127 на MacOS ventura, Windows 10 и Windows 11. Но только в конкретных ситуациях, которые я указал в ответе ...
А также на Edge 126.0.2592.87 на Windows 11
Я сообщил о проблеме в систему отслеживания ошибок Google, и они подтвердили, что могут воспроизвести эту проблему для версий начиная с 116 до текущей стабильной версии, а также для версий в будущих каналах выпуска бета-версии, dev и canary.
Проблема:
Вы пытаетесь сохранить и восстановить состояние флажка вместе с другими элементами, используя localStorage. Хотя он работает только с флажком, введение дополнительных элементов (например, текстовой области) может привести к тому, что состояние флажка не будет правильно сохраняться в Chrome и Edge при дублировании вкладок.
Причина:
Между рендерингом браузера и вашим кодом JavaScript существует состояние гонки. Когда вы загружаете данные из localStorage (с помощью функции загрузки), браузер может отображать такие элементы, как текстовая область, прежде чем устанавливать ее значение. Это может перезаписать сохраненное состояние флажка в localStorage, когда вы позже вызовете функцию сохранения.
Решение:
Есть два подхода к решению этой проблемы:
Использование setTimeout:
Оберните вызов функции загрузки внутри setTimeout с задержкой 0. Это гарантирует выполнение загрузки после завершения рендеринга браузера:
function load() {
setTimeout(function() {
var checked = localStorage.getItem('checkbox1zaal1') === 'true'; // No need for JSON.parse for booleans
document.getElementById("checkbox1zaal1").checked = checked;
var textarea = document.querySelector('textarea');
textarea.value = JSON.parse(localStorage.getItem('box'));
}, 0);
}
Использование события хранения:
Прослушивайте событие хранения объекта окна. Это событие срабатывает всякий раз, когда изменяется localStorage. Вы можете проверить изменения в конкретном ключе, который вы используете для флажка, и соответствующим образом обновить его состояние:
window.addEventListener('storage', function(event) {
if (event.key === 'checkbox1zaal1') {
var checked = event.newValue === 'true'; // No need for JSON.parse for booleans
document.getElementById("checkbox1zaal1").checked = checked;
}
});
load();
Дополнительные баллы:
Вы можете напрямую использовать localStorage.setItem('checkbox1zaal1', checkbox.checked) для хранения логических значений (проверенное состояние) в localStorage. Не забудьте очистить localStorage, когда пользователь явно удаляет данные (с помощью кнопки «Удалить»).
Верхняя половина этого ответа просто перефразирует мой ответ, полученный несколько дней назад. И часть с событием хранилища не имеет никакого смысла в этом вопросе, потому что речь идет не о синхронизации, и событие хранилища не будет вызываться при открытии новой вкладки.
Я не уверен, что это известная ошибка, но это ошибка со стороны Chrome. Я предполагаю, что это связано с тем, что эта duplication
функция не стандартизирована, и, очевидно, их метод борьбы с дублированием все еще не завершен.
Итак, прибегнем к обходным путям!
<body>
<input type = "button" id = "ReserveerButton1" value = "save" onclick = "save()">
<input type = "button" id = "Wisbutton1" value = "delete" onclick = "wis()">
<input type = "checkbox" id = "checkbox1zaal1">
<textarea>Hello, World!</textarea>
<script>
// Treat this as a constant, in real app, you can use `const`
// We're now limiting ourselves to es5
var CHECKBOX_NAME = 'checkbox-' + Math.random().toString().substring(2, 8);
var DEFAULT_TEXTAREA_VALUE = 'Hello, World!';
function save() {
var checkbox = document.getElementById('checkbox1zaal1');
var textarea = document.querySelector('textarea');
if (checkbox.checked) {
localStorage.setItem('checkbox1zaal1', '1');
} else {
localStorage.removeItem('checkbox1zaal1');
}
localStorage.setItem('box', textarea.value);
}
function load() {
var textarea = document.querySelector('textarea');
textarea.value = localStorage.getItem('box') || DEFAULT_TEXTAREA_VALUE;
var checkbox = document.getElementById('checkbox1zaal1');
checkbox.name = CHECKBOX_NAME;
localStorage.getItem('checkbox1zaal1') === '1' &&
checkbox.setAttribute('checked', 'checked');
}
function wis() {
location.reload();
localStorage.clear();
}
load();
</script>
</body>
Очевидно, использование другого флажка name
for позволяет браузеру правильно загрузить значение. Не волнуйтесь, хотя мы создаем флажок name
динамически выше, мы все равно можем отслеживать его, используя постоянный идентификатор.
Н.Б. Взял на себя смелость провести рефакторинг, систематизировать и устранить некоторые ошибки синтаксического анализа.
Вот попытка -
function load() {
var checked = JSON.parse(localStorage.getItem('checkbox1zaal1'));
if (checked) {
document.getElementById("checkbox1zaal1").click();
}
document.querySelector('textarea').value = JSON.parse(localStorage.getItem('box'));
}
Вместо установки атрибута checked
мы вызываем щелчок, чтобы они проверили этот элемент!
Н.Б.
setTimeout
для выполнения load
— такой же хороший обходной путь, как и следующий!Довольно сложный подход, если всю проблему можно решить с помощью простого setTimeout(load, 0)
и, возможно, это будет исправлено в ближайшем выпуске Chrome.
Я только что провел рефакторинг кода, чтобы кое-что исправить. Кроме этого, единственное, что здесь важно, — это прикрепить уникальный name
к вводу флажка. Не думайте, что это сложный подход. Если код вас смущает, возьмите код автора и попробуйте добавить следующую однострочную строку в функцию load
после первой строки функции: document.getElementById("checkbox1zaal1").name = 'checkbox-' + Math.random().toString ().подстрока(2, 8);
Нет, меня это не смущает, но установка случайных имен для формирования входных данных не всегда возможна, потому что обычно, когда кто-то использует имя для входных данных, это имя несет определенную семантику.
:) Очень распространенная проблема с vanilla js. Нам нужно подумать о трёх вещах. Изменение пользовательского интерфейса, изменение СОСТОЯНИЯ, а также сохранит ли пользователь это состояние или нет.
Поэтому я немного обновил ваш код. Пожалуйста, дайте мне знать, если вам нужно больше объяснений, кроме комментариев.
<!doctype html>
<html lang = "en">
<head>
<meta charset = "utf-8">
<meta name = "viewport" content = "width=device-width, initial-scale=1">
<title>Unsaved Checkbox State with localStorage in Chrome and Edge - Labground</title>
<link rel = "icon" href = "/images/lab.svg" type = "image/svg+xml">
</head>
<body>
<input type = "button" id = "ReserveerButton1" value = "save" onclick = "save()">
<input type = "button" id = "Wisbutton1" value = "delete" onclick = "wis()">
<input type = "checkbox" id = "checkbox1zaal1">
<textarea></textarea>
<script>
const DOM_ELEMENTS = {
checkbox: document.getElementById('checkbox1zaal1'),
textarea: document.querySelector('textarea')
}
const EL_STATE = {
checkbox: JSON.parse(localStorage.getItem('checkbox1zaal1')),
textarea: localStorage.getItem('box')
}
function wis() {
localStorage.clear();
location.reload();
}
// Save
function save() {
localStorage.setItem('checkbox1zaal1', DOM_ELEMENTS.checkbox.checked);
localStorage.setItem('box', DOM_ELEMENTS.textarea.value);
}
// Onload function.
(function() {
// Get checkbox state
if (EL_STATE.checkbox === null) {
EL_STATE.checkbox = false;
localStorage.setItem('checkbox1zaal1', EL_STATE.checkbox);
} else {
localStorage.setItem('checkbox1zaal1', EL_STATE.checkbox);
}
// UI checkbox state
DOM_ELEMENTS.checkbox.checked = EL_STATE.checkbox;
// Get textarea state
if (EL_STATE.textarea === null) {
EL_STATE.textarea = 'Hello World!!!';
localStorage.setItem('box', EL_STATE.textarea);
} else {
localStorage.setItem('box', EL_STATE.textarea);
}
// UI textarea state
DOM_ELEMENTS.textarea.value = EL_STATE.textarea;
// Listen on changes of the checkbox UI and UPDATE state each time the checkbox is change
DOM_ELEMENTS.checkbox.addEventListener('click', (event) => {
EL_STATE.checkbox = event.target.checked;
});
// Listen on changes of the textbox UI and UPDATE state each time the value is change
DOM_ELEMENTS.textarea.addEventListener('input', (event) => {
EL_STATE.textarea = event.target.value;
});
})();
</script>
</body>
</html>
Почему это «очень распространенная проблема»? На самом деле это не потому, что вся эта проблема возникает только в очень специфической ситуации, которая, скорее всего, связана с браузером, а не с кодом...
эй @derpirscher Мне плохо. Я не заключал в кавычки «очень распространенную проблему с JS» :) Я упомянул об этом, потому что мы забыли базовый процесс работы JS-движка, поскольку большинство из нас (включая меня) используем фреймворки, которые выполняют эту работу «из коробки», поскольку мы все ленивы :) . Я подробно описал этот код, чтобы показать, почему нам нужно обновлять UI и STATE отдельно, не предполагая, что значения являются типами по умолчанию.
@CBroe: «зачем анализировать это как JSON, если вы вообще никогда не кодировали его как JSON?» Согласно dfsq в его посте, он используется для изменения типа
string
наboolean
. Я просто пытался скопировать его скрипку.