Search code examples
javascripthtmllayoutcloneswitching

Dynamic Layout Cloning


I want to switch between 2 HTML page layouts (layout1 and layout2), whereat layout2 is derived (cloned) from layout1 since they are almost identical. I have simplified my project to this small example which shows my problem: layout1 does not appear again on the 2nd button click. Can someone please give me a hint why this does happen? Thank you very much in advance:

let active_layout = 1;

document.addEventListener('DOMContentLoaded', function () {
    const button = document.getElementById('button');
    const layout1 = document.getElementById('layout1');
    const layout2 = document.getElementById('layout2');

    layout2.classList.add('hidden');

    button.addEventListener('click', function () {
        if (active_layout === 1) {
            let elm_cloned = layout1.cloneNode(true);
            elm_cloned.querySelector('h1').textContent = 'Layout 2';
            elm_cloned.querySelector('p').textContent = 'This is layout 2.';

            layout2.innerHTML = '';
            layout2.appendChild(elm_cloned);

            layout1.classList.add('hidden');
            layout2.classList.remove('hidden');
  } else {
    layout1.classList.remove('hidden');
    layout2.classList.add('hidden');
  }

        active_layout = (active_layout === 1) ? 2 : 1;
    });
});
.layout1,
.layout2 {
    background-color: #f0f0f0;
    padding: 20px;
    margin: 10px;
}
.layout1.hidden,
.layout2.hidden {
    display: none;
}
<div class="layout1" id="layout1">
    <h1>Layout 1</h1>
    <button id="button">Switch Layout</button>
    <p>This is layout 1.</p>
</div>
<!-- Layout 2 -->
<div class="layout2" id="layout2">
    <!-- Content of Layout 2 will be cloned from Layout 1 during runtime -->
</div>


Solution

  • The problem is that event listeners are not cloned/transferred together with the DOM.

    So, if you move button outside of the layout1, it works just fine:

    let active_layout = 1;
    
    document.addEventListener('DOMContentLoaded', function () {
        const button = document.getElementById('button');
        const layout1 = document.getElementById('layout1');
        const layout2 = document.getElementById('layout2');
    
        layout2.classList.add('hidden');
    
        button.addEventListener('click', function () {
            if (active_layout === 1) {
                let elm_cloned = layout1.cloneNode(true);
                elm_cloned.querySelector('h1').textContent = 'Layout 2';
                elm_cloned.querySelector('p').textContent = 'This is layout 2.';
    
                layout2.innerHTML = '';
                layout2.appendChild(elm_cloned);
    
                layout1.classList.add('hidden');
                layout2.classList.remove('hidden');
      } else {
        layout1.classList.remove('hidden');
        layout2.classList.add('hidden');
      }
    
            active_layout = (active_layout === 1) ? 2 : 1;
        });
    });
    .layout1,
    .layout2 {
        background-color: #f0f0f0;
        padding: 20px;
        margin: 10px;
    }
    .layout1.hidden,
    .layout2.hidden {
        display: none;
    }
    <button id="button">Switch Layout</button>
    <div class="layout1" id="layout1">
        <h1>Layout 1</h1>
        <p>This is layout 1.</p>
    </div>
    <!-- Layout 2 -->
    <div class="layout2" id="layout2">
        <!-- Content of Layout 2 will be cloned from Layout 1 during runtime -->
    </div>

    Also, you should move adding/removing DOM elements to outside of event listener, and just manipulate style within the listener:

    let active_layout = 1;
    
    document.addEventListener('DOMContentLoaded', function () {
        const button = document.getElementById('button');
        const layout1 = document.getElementById('layout1');
        const layout2 = document.getElementById('layout2');
    
        layout2.classList.add('hidden');
    
        let elm_cloned = layout1.cloneNode(true);
        elm_cloned.querySelector('h1').textContent = 'Layout 2';
        elm_cloned.querySelector('p').textContent = 'This is layout 2.';
    
        layout2.innerHTML = '';
        layout2.appendChild(elm_cloned);
    
        button.addEventListener('click', function () {
          if (active_layout === 1) {
            layout1.classList.add('hidden');
            layout2.classList.remove('hidden');
          } else {
            layout1.classList.remove('hidden');
            layout2.classList.add('hidden');
          }
    
            active_layout = (active_layout === 1) ? 2 : 1;
        });
    });
    .layout1,
    .layout2 {
        background-color: #f0f0f0;
        padding: 20px;
        margin: 10px;
    }
    .layout1.hidden,
    .layout2.hidden {
        display: none;
    }
    <button id="button">Switch Layout</button>
    <div class="layout1" id="layout1">
        <h1>Layout 1</h1>
        <p>This is layout 1.</p>
    </div>
    <!-- Layout 2 -->
    <div class="layout2" id="layout2">
        <!-- Content of Layout 2 will be cloned from Layout 1 during runtime -->
    </div>