Несохраненное состояние флажка с помощью localStorage в Chrome и Edge

Я пытаюсь сохранить и восстановить состояние 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>

ДЕМО

Действия по воспроизведению проблемы:

  1. Перейдите на демо-страницу.
  2. Просто установите флажок.
  3. Нажмите кнопку «Сохранить».
  4. Дублируйте вкладку.

Флажок на вкладке дубликатов не установлен.

Что мне не хватает? Это известная ошибка в Chrome и Edge, или есть другой подход, который я могу попробовать, чтобы гарантировать, что состояние checkbox сохраняется правильно вместе с другими элементами?

🤔 А знаете ли вы, что...
С JavaScript можно создавать интерактивные формы и проверять введенные пользователем данные.


1
346
6

Ответы:

ДОБАВЛЯТЬ :

var as = document.getElementById('textbox').value
//Code to sent to discord webhook
sent(as)

Я выполнил шаги, которые вы предоставили, чтобы воспроизвести проблему, но это не воспроизвело ее. Но когда я отключил настройку «Разрешить сайтам сохранять и читать данные cookie (рекомендуется)», я смог воспроизвести проблему. Поэтому введите «edge://settings/content/cookies» в адресной строке 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)


Проблема:

Вы пытаетесь сохранить и восстановить состояние флажка вместе с другими элементами, используя 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 мы вызываем щелчок, чтобы они проверили этот элемент!

Н.Б.

  • Вам все еще нужно исправить JSON-анализ вашего значения текстовой области, здесь я рассматриваю только получение состояния флажка!
  • Очевидно, это все еще не работает в Edge. Кажется, что использование setTimeout для выполнения load — такой же хороший обходной путь, как и следующий!

:) Очень распространенная проблема с 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>