1

While trying new things, which can make your life easier, I saw something that caught my eye. When you select some part of the page and then right-click on it, you can print this specific part. So.. my guess was "Use selection API and then print it!", but it didn't really go so well. I don't really know why and when, but I'm losing focus when trying to use window.print()
This is sample code which I was trying in console on stackoverflow. Any idea how to make it work? Maybe window.print() is wrong method to use here?

setTimeout(function () {
    var el = document.getElementsByClassName('default prettyprint prettyprinted')[0];
    window.getSelection().selectAllChildren(el);
    window.print();
}, 3000);

What I'm selecting and then click printing here I'm am clicking print What I get printed And what I get

Leo Odishvili
  • 1,004
  • 1
  • 7
  • 29

1 Answers1

2

You are losing focus because the browser will start the native printing api, which usually also includes opening a print dialog or print preview. When interacting with that dialog with a click the text loses focus.(in Chrome, other browsers not tested)

With the use of monitorEvents I can show you "what happens"

monitorEvents(window);
 window.getSelection().selectAllChildren(document.getElementsByClassName('post-text')[0]);
window.print();


pointerover PointerEvent {isTrusted: true, pointerId: 1, width: 1, height: 1, pressure: 0, …}
VM324:1 mouseover MouseEvent {isTrusted: true, screenX: -1644, screenY: 153, clientX: 276, clientY: 60, …}
VM324:1 pointermove PointerEvent {isTrusted: true, pointerId: 1, width: 1, height: 1, pressure: 0, …}
VM324:1 mousemove MouseEvent {isTrusted: true, screenX: -1644, screenY: 153, clientX: 276, clientY: 60, …}
VM324:1 pointerdown PointerEvent {isTrusted: true, pointerId: 1, width: 1, height: 1, pressure: 0.5, …}
VM324:1 mousedown MouseEvent {isTrusted: true, screenX: -1644, screenY: 153, clientX: 276, clientY: 60, …}
VM324:1 pointermove PointerEvent {isTrusted: true, pointerId: 1, width: 1, height: 1, pressure: 0.5, …}
VM324:1 mousemove MouseEvent {isTrusted: true, screenX: -1634, screenY: 205, clientX: 286, clientY: 112, …}
VM324:1 pointerup PointerEvent {isTrusted: true, pointerId: 1, width: 1, height: 1, pressure: 0, …}
VM324:1 mouseup MouseEvent {isTrusted: true, screenX: -1634, screenY: 205, clientX: 286, clientY: 112, …}

VM324:1 click MouseEvent {isTrusted: true, screenX: -1634, screenY: 205, clientX: 286, clientY: 112, …} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Using chrome in this example I clicked a button in the printing dialog UI. This mouseup event caused the focus to go away and apparently the selection ending up cancelled. Possibly chrome inserts the printing dialog as a shadow DOM object, letting interactions on it having events for the document.

What I found out is that if I use the escape key to exit the dialog that this event is not transferred to the global event listeners, causing it to not having any effect and letting the selection persist.

So, if you wanted to print that selection you could use a code like the code below:

+function() {
    // Create an iframe to make sure everything is clean and ordered.
    var iframe = document.createElement('iframe');
    // Give it enough dimension so you can visually check when modifying.
    iframe.width = document.width;
    iframe.height = document.height;
    // Add it to the current document to be sure it has the internal objects set up.
    document.body.append(iframe);

    // Get the node you wish to print.
    var origNode = document.querySelectorAll('.post-text .default.prettyprint.prettyprinted')[0];

    // Clone it and all it's children
    var node = origNode.cloneNode(true);


    /**
     * copied from  https://stackoverflow.com/questions/19784064/set-javascript-computed-style-from-one-element-to-another
     * @author Adi Darachi https://stackoverflow.com/users/2318881/adi-darachi
     */
    var copyComputedStyle = function(from,to){
        var computed_style_object = false;
        //trying to figure out which style object we need to use depense on the browser support
        //so we try until we have one
        computed_style_object = from.currentStyle || document.defaultView.getComputedStyle(from,null);

        //if the browser dose not support both methods we will return null
        if(!computed_style_object) return null;

            var stylePropertyValid = function(name,value){
                        //checking that the value is not a undefined
                return typeof value !== 'undefined' &&
                        //checking that the value is not a object
                        typeof value !== 'object' &&
                        //checking that the value is not a function
                        typeof value !== 'function' &&
                        //checking that we dosent have empty string
                        value.length > 0 &&
                        //checking that the property is not int index ( happens on some browser
                        value != parseInt(value)

            };

        //we iterating the computed style object and compy the style props and the values
        for(property in computed_style_object)
        {
            //checking if the property and value we get are valid sinse browser have different implementations
                if(stylePropertyValid(property,computed_style_object[property]))
                {
                    //applying the style property to the target element
                        to.style[property] = computed_style_object[property];

                }   
        }   

    };
    // Copy the base style.
    copyComputedStyle(origNode, node);

    // Copy over all relevant styles to preserve styling, work the way down the children tree.
    var buildChild = function(masterList, childList) {
        for(c=0; c<masterList.length; c++) {
           var master = masterList[c];
           var child = childList[c];
           copyComputedStyle(master, child);
           if(master.children && master.children.length > 0) {
               buildChild(master.children, child.children);
           }
        }
    }
    if(origNode.children && origNode.children.length > 0) {
        buildChild(origNode.children, node.children);
    }
    // Add the styled clone to the iframe. using contentWindow.document since it seems the be the most widely supported version.
    iframe.contentWindow.document.body.append(node);
    // Print the window
    iframe.contentWindow.print();
    // Give the browser a second to gather the data then remove the iframe.
    window.setTimeout(function() {iframe.parentNode.removeChild(iframe)}, 1000);
}();
Tschallacka
  • 27,901
  • 14
  • 88
  • 133
  • Yes, but when you are right-clicking and printing it, it opens print preview, but doesn't lose focus. Are you saying, that printing by right-click or Ctrl+P is not using native printing api? – Leo Odishvili Aug 01 '18 at 10:21
  • @LeoOdishvili added an possible explanation. – Tschallacka Aug 01 '18 at 10:48
  • thanks! Made a lot of sense, but I had another question, can you print some part of page, like chrome does it. If question wasn't so clear, I added some screenshots – Leo Odishvili Aug 01 '18 at 11:08
  • @LeoOdishvili I updated my answer. If you copy and paste the snippet in the console it will print your snippet. – Tschallacka Aug 01 '18 at 11:48
  • it works and I accepted the answer, but what I was trying to achieve is still not clear to me, seems it can't be achieved. What I wanted is to select some part of page with selection API and then print it. Walked through internet and didn't find an answer, so I think yours is the closest one. Thank you :) – Leo Odishvili Aug 01 '18 at 11:54
  • Well, you can iterate through all the nodes in the selection and add those to the iframe and then print it. https://stackoverflow.com/questions/7781963/js-get-array-of-all-selected-nodes-in-contenteditable-div – Tschallacka Aug 01 '18 at 12:00