Когда вы дублируете вкладку в браузере, текущие значения элементов form
игнорируются. Протестировано в последних версиях Chrome, Firefox и Edge на компьютере с Windows 11.
var textarea = document.querySelector('textarea');
var p = document.querySelector('p');
var range = document.querySelector('input[type = "range"]');
var output = document.querySelector('output');
var checkbox = document.querySelector('input[type = "checkbox"]');
var span = document.querySelector('span');
var theme = document.querySelector('select');
function write() {
p.textContent = textarea.value;
output.value = range.value;
span.textContent = checkbox.checked;
document.body.className = theme.value;
}
textarea.addEventListener('input', write);
range.addEventListener('input', write);
checkbox.addEventListener('change', write);
theme.addEventListener('change', write);
write();
body {
display: grid;
grid-template-columns: repeat(2, max-content);
gap: 1em 0.5em;
}
body.Dark {
color: white;
background: black;
}
<textarea>Hello, world!</textarea>
<p></p>
<input type = "range">
<output></output>
<input type = "checkbox">
<span></span>
<select>
<option>Light</option>
<option>Dark</option>
</select>
textarea
, input
s и select
.p
, output
и span
не отображают ожидаемое текстовое содержимое, а тема по-прежнему светлая.@pilchard: Спасибо, но это не то же самое, и это не дает рабочего решения.
«Изменить текстовое поле и ввести значения по умолчанию». - ввод данных в эти поля не изменяет их значения по умолчанию, а только их текущее значение.
То, что вы хотите здесь, просто не является поведением по умолчанию. Если вам это нужно, вам придется облегчить это самостоятельно — сохранять текущие значения полей в localStorage
всякий раз, когда они изменяются, а затем, когда второй экземпляр вашей страницы загружается в новую вкладку — считывать значения обратно из localStorage
и устанавливать их для поля ввода в этом документе.
Использование localStorage для сохранения значения и восстановления его обратно на дублирующей вкладке будет работать, но если вы сделаете несколько дублирующих вкладок, будет сложно найти, какое значение вкладки нужно восстановить.
@CBroe: Как насчет sessionStorage
?
@ Мори, как насчет этого? developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage: «Всякий раз, когда документ загружается на определенной вкладке в браузере, создается уникальный сеанс страницы, который назначается этому конкретному вкладка. Этот сеанс страницы действителен только для этой конкретной вкладки». - так явно не подходит для того, что вы хотите здесь.
@CBroe: из того же источника: «Дублирование вкладки копирует sessionStorage вкладки в новую вкладку».
Хорошо, если это единственный вариант использования, для которого вам это нужно (а не, скажем, если пользователь вручную открывает тот же URL-адрес на другой вкладке), это также подойдет.
var textarea = document.querySelector('textarea');
var p = document.querySelector('p');
var range = document.querySelector('input[type = "range"]');
var output = document.querySelector('output');
var checkbox = document.querySelector('input[type = "checkbox"]');
var span = document.querySelector('span');
document.querySelector('textarea').value = 'Hello, world!';
document.querySelector('input[type = "range"]').value = 50;
document.querySelector('input[type = "checkbox"]').checked = false;
function write() {
p.textContent = textarea.value;
output.textContent = range.value;
span.textContent = checkbox.checked;
}
textarea.addEventListener('input', write);
range.addEventListener('input', write);
checkbox.addEventListener('change', write);
write();
body {
display: grid;
grid-template-columns: repeat(2, max-content);
gap: 1em 0.5em;
}
<textarea>Hello, world!</textarea>
<p></p>
<input type = "range">
<output></output>
<input type = "checkbox">
<span></span>
Когда браузер дублирует вкладки, он не запускает событие изменения textarea/range/checkbox. После дублирования вкладки при загрузке dom установит свое значение. Итак, вам нужно обеспечить некоторую задержку для вашей функции write
, чтобы браузер завершил настройку содержимого элемента, а затем наша функция записи получила правильное значение элемента.
Обновлен код следующим образом:
<!DOCTYPE html>
<html lang = "en">
<head>
<meta http-equiv = "Content-Type" content = "text/html; charset=UTF-8">
<title>Duplicate browser tab ignores current values</title>
<style>
body {
display: grid;
grid-template-columns: repeat(2, max-content);
gap: 1em 0.5em;
}
</style>
</head>
<body>
<textarea>Hello, world!</textarea>
<p></p>
<input type = "range">
<output></output>
<input type = "checkbox">
<span></span>
<script>
var textarea = document.querySelector('textarea');
var p = document.querySelector('p');
var range = document.querySelector('input[type = "range"]');
var output = document.querySelector('output');
var checkbox = document.querySelector('input[type = "checkbox"]');
var span = document.querySelector('span');
function write() {
p.textContent = textarea.value;
output.textContent = range.value;
span.textContent = checkbox.checked;
}
textarea.addEventListener('input', write);
range.addEventListener('input', write);
checkbox.addEventListener('change', write);
setTimeout(function() {
write();
}, 10);
</script>
</body>
</html>
Но Chrome не копирует состояние флажка на дублированной вкладке.
Чтобы определить, дублируется ли вкладка или нет в Chrome/Firefox/IE (Chromium), вы можете использовать следующий JS:
// type value => details
// 0 => new tab
// 1 => reload tab
// 2 => duplicate tab
window.performance.navigation.type == 2
«Но Chrome не копирует состояние флажка на дублированной вкладке». Он ведет себя непоследовательно: иногда работает, а иногда нет.
Хорошо. Мы не можем использовать localStorage. Как если бы мы продублировали вкладку один раз, мы получили бы данные из localStorage и установили. После дублирования мы меняем значение в дублированной вкладке, и теперь мы дублируем вкладку из первой, тогда localStorage будет иметь значение из последнего изменения, поэтому в форме будут отображаться неправильные данные.
У нас такая же проблема с sessionStorage
?
Вы можете использовать sessionStorage. Но если вы дублируете вкладку и вносите изменения, а затем снова дублируете исходную вкладку, вы получаете данные из дублированной вкладки. localStorage будет использоваться всеми вкладками одного домена.
Более уместно вызывать функцию write()
для события window.onload
. Но это все равно не установит свойство :checked
, а только обновит textContent
в элементах.
Интересно, почему вы добавили текстовое содержимое по умолчанию в элементы p
, output
и span
? Разве это не лишнее?
@Mori Он уже добавлен в образец. Но его можно удалить, так как метод записи всегда будет вызываться при загрузке страницы. Таким образом, он обновит p
, output
и span
. Обновленный ответ.
@imhvost Не работает с загрузкой. Поскольку браузер устанавливает значение ввода/диапазона после загрузки. Поэтому я использовал тайм-аут.
Я не уверен, но можно ли добиться этой задержки с помощью функции async
вместо setTimeout
?
Нет, насколько я знаю. Если мы добавим async
для записи функции без setTimeout
, она будет вести себя так же, как и у вас. Если вы добавите ожидание при вызове функции записи, она перестанет работать.
Задержка 10
мс — это просто произвольное число, и фактическое время отличается от браузера к браузеру. Может ли рассчитано время, необходимое браузеру для загрузки текущего значения в дублирующую вкладку?
Это также зависит от того, насколько велик ваш сайт. В настоящее время с вашим образцом работает 10 мс. Но нужно проверить реальный сайт и другой браузер и найти оптимальное значение.
Проверил - нормально работает на window.onload
. Так что setTimeout
не нужен. Но это все равно не установит :checked
, только обновит textContent
в элементах.
При дублировании вкладки сохраняются значения полей ввода, за исключением :checked
props.
Как вариант, вы можете сохранить :checked
реквизит в sessionStorage
.
Для тестирования я создал небольшой пример.
Я разделил скрипты на три части:
:checked
Я добавляю весь код здесь:
Обновление: добавлена тема смены.
<!DOCTYPE html>
<html lang = "en">
<head>
<title>Duplicate browser tab ignores current values</title>
<style>
body.dark {
color: white;
background-color: black;
}
</style>
</head>
<body>
<ul>
<li>
<textarea>Hello, world!</textarea>
<span></span>
</li>
<li>
<input type = "range">
<span></span>
</li>
<li>
<input type = "checkbox">
<span></span>
</li>
<li>
<input type = "radio" name = "radio" value = "1" checked>
<input type = "radio" name = "radio" value = "2">
<span></span>
</li>
<li>
<select>
<option>light</option>
<option>dark</option>
</select>
<span></span>
</li>
</ul>
<script>
/* save and restore :checked */
const checkedElements = document.querySelectorAll('[type = "checkbox"], [type = "radio"]');
window.addEventListener('load', restoreCheckedState)
function restoreCheckedState() {
if (sessionStorage.getItem('checkedState')) {
const storage = JSON.parse(sessionStorage.getItem('checkedState'));
checkedElements.forEach( (el, index) => el.checked = storage[index] );
// console.info('restore', sessionStorage.getItem('checkedState'));
}
}
checkedElements.forEach( el => el.addEventListener('change', saveCheckedState) );
function saveCheckedState() {
const checkeds = [];
checkedElements.forEach( el => checkeds.push(el.checked) );
sessionStorage.setItem( 'checkedState', JSON.stringify(checkeds) );
// console.info('saved', sessionStorage.getItem('checkedState'));
}
/* initial show values */
window.addEventListener('load', () => {
inputs.forEach( el => showInputValue(el) );
changeTheme( document.querySelector('select').value );
})
/* show value after input change */
const inputs = document.querySelectorAll('input, textarea, select');
inputs.forEach( el => el.addEventListener( 'input', () => showInputValue(el) ) );
function showInputValue(input) {
const span = input.closest('li').querySelector('span');
if ( input.type === 'checkbox' ) {
span.textContent = input.getAttribute('value') ? input.value : input.checked;
} else if ( input.type === 'radio' ) {
if ( input.name ){
span.textContent = document.querySelector(`[name = "${input.name}"]:checked`).value
} else {
span.textContent = input.checked;
}
} else {
span.textContent = input.value;
}
}
/* theme change */
document.querySelector('select').addEventListener('change', function() {
changeTheme(this.value);
})
function changeTheme(theme = 'light') {
document.body.classList.remove('light', 'dark');
document.body.classList.add(theme);
}
</script>
</body>
</html>
Я заметил, что дублирование флажков и радиокнопок работает надежно, если value
флажка/радиокнопки перезаписывается.
Флажок и текстовое поле правильно дублируются вместе с их текстовыми представлениями в следующем примере. Тема тоже дублируется.
function update() {
for (var i of document.querySelectorAll("input")) {
if (i.type === "checkbox" || i.type === "radio") {
var value = i.value;
i.value = "";
i.value = value;
}
i.nextElementSibling.textContent = i.value;
}
document.body.className = document.querySelector("select").value;
}
body.Dark {
color: white;
background: black;
}
<body onload = "update()">
<input type = "checkbox" onchange = "update()" /><span></span>
<input type = "radio" name = "radio" value = "A" onchange = "update()" /><span></span>
<input type = "radio" name = "radio" value = "B" onchange = "update()" /><span></span>
<input type = "text" onchange = "update()" /><span></span>
<select onchange = "update()">
<option>Light</option>
<option>Dark</option>
</select>
</body>
А как же <input type = "radio">
?
Кажется, это работает. Но следует учитывать, что в реальном случае <input type = "radio">
будет иметь значение, отличное от "on"
.
Конечно. Я снова отредактировал свой ответ.
Я проверил шаги, которые вы предоставили, в Firefox и Brave (на основе Chromium), и они оба ведут себя по-разному при дублировании вкладок.
'change'
слушаете 'input'
на своем <select>
. Причина в том, что 'change'
срабатывает только тогда, когда пользователь активно выбирает значение, чего не происходит во время дублирование.)Эта разница в поведении хорошо показывает, что «дублирование вкладок» не стандартизировано, а представляет собой функцию, которую каждый поставщик браузера реализует так, как он видит лучше всего. Каждый браузер решает, какая часть исходного состояния вкладки будет дублироваться, что может иметь последствия для UX, производительности и, возможно, даже безопасности, и, как показало мое тестирование, степень дублирования может существенно различаться в разных браузерах.
Итак, как видите, нельзя полагаться на идеальное дублирование или согласованность в разных браузерах. Это означает, что вам нужно какое-то постоянство, чтобы убедиться, что значения в форме сохраняются и повторно используются на дублированной вкладке.
Как уже упоминалось, вы можете использовать sessionStorage или localStorage для тестирования или сохранить значения в какой-либо базе данных в качестве более надежного решения.
write()
должен вызываться при начальной загрузке сайта.
document.addEventListener("DOMContentLoaded",()=>{
write();
})
Это должно работать.
- Почему это происходит?
Потому что форма на вкладке браузера не сохраняет текущее значение.
- Какое решение?
Два решения для решения этой проблемы:
1. Использование параметров запроса
Параметры запроса будут хранить текущее значение как Параметры поиска URL, чтобы к нему можно было получить доступ для пополнения данных при дублировании вкладки браузера с помощью new URL(document.location).searchParams
.
Код: https://playcode.io/queryparams
Демо: https://queryparams.playcode.io
<!DOCTYPE html>
<html>
<head>
<title>Duplicate browser tab ignores form elements current values</title>
<style>
body {
display: grid;
grid-template-columns: repeat(2, max-content);
gap: 1em 0.5em;
}
body.Dark {
color: white;
background: black;
}
</style>
</head>
<body>
<textarea>Hello, world!</textarea>
<p></p>
<input type = "range" />
<output></output>
<input type = "checkbox" />
<span></span>
<select>
<option>Light</option>
<option>Dark</option>
</select>
<script>
var textarea = document.querySelector("textarea");
var p = document.querySelector("p");
var range = document.querySelector('input[type = "range"]');
var output = document.querySelector("output");
var checkbox = document.querySelector('input[type = "checkbox"]');
var span = document.querySelector("span");
var theme = document.querySelector("select");
let currentParams = new URL(document.location).searchParams;
function createQueryParams() {
let newParams = new URLSearchParams({
textarea: textarea.value,
range: range.value,
checkbox: checkbox.checked,
theme: theme.value,
});
window.history.pushState("", "", `${location.pathname}?${newParams}`);
}
function applyQueryParams() {
textarea.value = currentParams.get("textarea") !== undefined ? currentParams.get("textarea") : textarea.value;
range.value = currentParams.get("range") ? currentParams.get("range") : range.value;
checkbox.checked = currentParams.get("checkbox") ? (currentParams.get("checkbox") == 'true') : checkbox.checked;
theme.value = currentParams.get("theme") ? currentParams.get("theme") : theme.value;
write();
}
function write() {
textarea.innerHTML = textarea.value;
p.textContent = textarea.value;
output.textContent = range.value;
span.textContent = checkbox.checked;
document.body.className = theme.value;
createQueryParams();
}
textarea.addEventListener("input", write);
range.addEventListener("input", write);
checkbox.addEventListener("change", write);
theme.addEventListener("change", write);
applyQueryParams();
</script>
</body>
</html>
2. Использование хранилища сеансов
Хранилище сеанса будет хранить текущее значение как данные сеанса, чтобы к нему можно было получить доступ для пополнения данных при дублировании вкладки браузера методом .getItem
.
Код: https://playcode.io/sessionstorage
Демо: https://sessionstorage.playcode.io
<!DOCTYPE html>
<html>
<head>
<title>Duplicate browser tab ignores form elements current values</title>
<style>
body {
display: grid;
grid-template-columns: repeat(2, max-content);
gap: 1em 0.5em;
}
body.Dark {
color: white;
background: black;
}
</style>
</head>
<body>
<textarea>Hello, world!</textarea>
<p></p>
<input type = "range" />
<output></output>
<input type = "checkbox" />
<span></span>
<select>
<option>Light</option>
<option>Dark</option>
</select>
<script>
var textarea = document.querySelector("textarea");
var p = document.querySelector("p");
var range = document.querySelector('input[type = "range"]');
var output = document.querySelector("output");
var checkbox = document.querySelector('input[type = "checkbox"]');
var span = document.querySelector("span");
var theme = document.querySelector("select");
let currentSession = JSON.parse(sessionStorage.getItem('data')) || {};
function createSessionStorage() {
let newSession = {
textarea: textarea.value,
range: range.value,
checkbox: checkbox.checked,
theme: theme.value,
};
sessionStorage.setItem('data', JSON.stringify(newSession));
}
function applySessionStorage() {
textarea.value = currentSession["textarea"] ? currentSession["textarea"] : textarea.value;
range.value = currentSession["range"] ? currentSession["range"] : range.value;
checkbox.checked = currentSession["checkbox"] ? currentSession["checkbox"] : checkbox.checked;
theme.value = currentSession["theme"] ? currentSession["theme"] : theme.value;
write();
}
function write() {
textarea.innerHTML = textarea.value;
p.textContent = textarea.value;
output.textContent = range.value;
span.textContent = checkbox.checked;
document.body.className = theme.value;
createSessionStorage();
}
textarea.addEventListener("input", write);
range.addEventListener("input", write);
checkbox.addEventListener("change", write);
theme.addEventListener("change", write);
applySessionStorage();
</script>
</body>
</html>
Отвечает ли это на ваш вопрос? Продублировать вкладку в Chrome без перезагрузки страницы?