重复的浏览器选项卡忽略表单元素当前值

问题描述 投票:0回答:6

问题

当您在浏览器中复制选项卡时,

form
元素的当前值将被忽略。在 Windows 11 计算机上的最新版本的 Chrome、Firefox 和 Edge 中进行了测试。

示例代码和演示

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.textContent = 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>

演示

重现问题的步骤

  1. 打开演示页面或创建您自己的。
  2. 更改
    textarea
    input
    s和
    select
    默认值。
  3. 复制标签。

  1. p
    output
    span
    元素没有显示预期的文本内容,主题仍然很淡。

问题

  1. 为什么会这样?
  2. 解决方案是什么?
javascript html forms browser tabs
6个回答
0
投票

    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>


0
投票

浏览器复制tab时,不会触发textarea/range/checkbox的change事件。之后,复制选项卡,当加载 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

0
投票

复制选项卡时,输入字段值将被保存,但

:checked
道具除外。
作为一个选项,您可以将
:checked
道具保存在
sessionStorage
.

为了测试,我创建了一个小的example.
我将脚本分为三个部分:

  1. 保存和恢复
    :checked
  2. 初始显示输入值
  3. 输入更改后显示值

我在这里添加所有代码:

更新:添加更改主题。

<!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.log('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.log('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>


0
投票

我观察到,如果复选框/单选按钮的

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>


0
投票

我测试了您在 Firefox 和 Brave(基于 Chromium)上提供的步骤,它们都在选项卡复制上表现不同

  • Firefox 保留“表单”值并调用事件侦听器以反映更改(仅当您而不是
    'change'
    在您的
    'input'
    上收听
    <select>
    时才有效。原因是
    'change'
    仅在用户主动时触发选择一个值,在复制过程中不会发生。)
  • Brave 保留“表单”值但不调用事件侦听器,因此您的输出元素不会反映初始值(并且主题不会更改)。

这种行为上的差异很好地表明“选项卡重复”不是标准化的,而是每个浏览器供应商以其认为最好的方式实现的功能。每个浏览器都可以决定有多少原始选项卡的状态被复制,这可能会对用户体验、性能甚至安全产生影响,而且正如我的测试所示,不同浏览器的复制程度可能会有很大差异。

如您所见,一个 不能依赖于跨不同浏览器的完美复制或一致性。这意味着您需要某种持久性来确保表单中的值得到保存并在重复的选项卡中重复使用。

正如其他人已经提到的,您可以使用 sessionStoragelocalStorage 进行测试或将值保存在某种数据库中作为更强大的解决方案。


0
投票

write()
必须在初始站点加载时调用。

document.addEventListener("DOMContentLoaded",()=>{
    write();
})

这应该有效。

© www.soinside.com 2019 - 2024. All rights reserved.