skip to Main Content

Issue

When you duplicate a tab in your browser, the current values of form elements are ignored. Tested in the latest versions of Chrome, Firefox and Edge on a Windows 11 computer.

Sample code & demo

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>

DEMO

Steps to reproduce the issue

  1. Open the demo page or create your own.
  2. Change the textarea, inputs, and select default values.
  3. Duplicate the tab.

enter image description here

  1. The p, output and span elements don’t show the expected text content and the theme is still light.

Question

  1. Why does it happen?
  2. What’s the solution?

5

Answers


  1.     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>
    Login or Signup to reply.
  2. When the browser duplicates tabs, it will not fire the change event of textarea/range/checkbox. After, duplicating the tab, when the dom is loaded it will set its value. So, you need to provide some delay for your write function so the browser finishes setting the content of an element and then our write function will get the proper value of an element.

    Updated code as following:

    <!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>
    

    But Chrome is not copying state of checkbox in duplicated tab.

    To detect if tab is duplicated or not on Chrome/Firefox/IE(Chromium) you can use following JS:

    // type value => details
    // 0 => new tab
    // 1 => reload tab
    // 2 => duplicate tab
    window.performance.navigation.type == 2
    
    Login or Signup to reply.
  3. When duplicating a tab, input fields values are saved, with exception :checked props.
    As an option, you can save :checked props in sessionStorage.

    For testing, I created a small example.
    I divided the scripts into three parts:

    1. save and restore :checked
    2. initial show inputs values
    3. show value after input change

    I am adding all the code here:

    Update: added change theme.

    <!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>
    Login or Signup to reply.
  4. I observed that the duplication of checkboxes and radiobuttons works reliably if the value of the checkbox/radiobutton is overwritten.

    Both checkbox and text field are duplicated correctly, along with their textual representations, in the following example. The theme is also duplicated.

    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>
    Login or Signup to reply.
  5. I tested the steps you provided on Firefox and Brave (based on Chromium) and they both behave differently on tab duplication.

    • Firefox persists both "form" values and calls event listeners to reflect the change (works only when you instead of 'change' listen to 'input' on your <select>. The reason is that 'change' only fires when user actively selects a value, which doesn’t happen during duplication.)
    • Brave keeps the "form" values but doesn’t call the event listeners, so your output elements don’t reflect the initial values (and the theme doesn’t get changed).

    This difference in behaviour shows well that the "tab duplication" isn’t standardized, but rather a feature that each browser vendor implements the way it sees best. Each browser gets to decide how much of the original tab’s state gets duplicated, which can have UX, performance and perhaps even security implications, and as my testing has shown the degree of duplication can differ substantially across browsers.

    So as you can see, one can’t rely on a perfect duplication nor consistency across different browsers. This means that you need some kind of persistency to make sure the values in the form get saved and reused in duplicated tab.

    As others have already mentioned, you can use sessionStorage or localStorage for testing or save the values in some kind of a database as a more robust solution.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search