Search code examples
javascriptdomprintinginnerhtml

Page not responsive after setting innerHTML of dom


I am creating a page where I want to print a specific div. I am using the following code (see also this post)

var printContents = document.querySelector('.mx-name-PrintArea').innerHTML;
var originalContents = document.body.innerHTML;
document.body.innerHTML = printContents;
window.print();
document.body.innerHTML = originalContents;
window.reload();

In the basis, this works like a charm. I can print the selected div. However, when I return to the original page, I run into problems. The page looks the same, though it is no longer responsive. My buttons are no longer working, for example. It seems that the scripts are no longer active.

Anyone knows how to solve this?


Solution

  • First off, this is fundamentally not how you handle making a page printable. Instead, if the page has separate viewing and printing content, have the viewing contents of the page in a container with a non-print media query, and have the printable contents in a container with a print media query. More about media queries here.

    CSS:

    @media not print {
        .printing-container {
            display: none;
        }
    }
    @media print {
        .viewing-container {
            display: none;
        }
    }
    

    HTML:

    <body>
        <div class="viewing-container">
        Content just for viewing here
        </div>
        <div class="printing-container">
        Content just for printing here
        </div>
    </body>
    

    That's if you want to have completely separate content. Otherwise, use more specific queries to just hide/show individual parts of the page.


    But with what you're currently doing, the problem is that you're destroying and recreating the elements from an HTML string, so the event handlers get lost (and some other state information).

    Instead, move the elements to a document fragment, and then back to the body when done. For instance:

    Moving them to the fragment:

    const frag = document.createDocumentFragment();
    while (document.body.firstChild) {
        frag.appendChild(document.body.firstChild); // Moves it
    }
    

    Moving them back:

    document.body.innerHTML = "";
    while (frag.firstChild) {
        document.body.appendChild(frag.firstChild); // Moves it
    }
    

    Since they're the same elements, they'll still have their event handlers and such.

    Live Example:

    // Initial page setup with an event handler
    document.getElementById("btn-example").addEventListener("click", function() {
        console.log("Example button clicked");
        const frag = saveContents(document.body);
        document.body.innerHTML = "<p>This is the temporary DOM content</p>";
        setTimeout(() => restoreContents(document.body, frag), 1000);
    });
    
    function moveContents(from, to) {
        while (from.firstChild) {
            to.appendChild(from.firstChild);
        }
    }
    
    function saveContents(element) {
        const frag = document.createDocumentFragment();
        moveContents(element, frag);
        return frag;
    }
    
    function restoreContents(element, frag) {
        element.innerHTML = "";
        moveContents(frag, element);
    }
    <p>This is the original viewing content</p>
    <input type="button" id="btn-example" value="Click Me">