Andrey Prokhorov
Andrey Prokhorov

Reputation: 919

Cannot save formatted data (text/html) in the clipboard in Safari

I want to copy a part of HTML page to the clipboard and keep the formatting.

I googled for a solution based on execCommand API (i.e document.execCommand("copy")) and that should work on all major browsers according to mozilla developer docs and caniuse.com, so that even Safari 10+ should support that.

See an example below that works in Chrome v.68, Firefox v.61, Edge v.42, but not in the latest Safari 11.1.2 (13605.3.8):

https://jsfiddle.net/andreyprokhorov/xpvt214o/773246/

There are plenty of js libraries out there (for ex: https://clipboardjs.com), but most solutions rely on execCommand APIs (already trying here) or on selection - i.e. create an invisible textarea, this does work in Safari but I lose the formatting.

Who knows if it is possible to do in Safari?

<div id="data">
<b>Example HTML data</b>  
</div>

<button>Save to clipboard</button>

$(function() {

    function copyToClipboard(data) {
        function listener(e) {
            e.clipboardData.setData("text/html", data);
            e.clipboardData.setData("text/plain", data);
            e.preventDefault();
        }

        document.addEventListener("copy", listener);
        document.execCommand("copy");
        document.removeEventListener("copy", listener);
    };

    function copyHtmlToClipboard() {
        var data = document.getElementById("data").innerHTML;
        copyToClipboard(data);
    };


    var button = $("button")
    button.on("click", copyHtmlToClipboard)

});

Upvotes: 0

Views: 935

Answers (1)

Andrey Prokhorov
Andrey Prokhorov

Reputation: 919

According to this answer:

For security reasons iOS Safari only allows document.execCommand('copy') for text within a contentEditable container.

I added some code to make the div editable and then select all elements inside it:

See https://jsfiddle.net/andreyprokhorov/xpvt214o/774620/

$(function() {

    function selectElementContents(el) {
        var range = document.createRange();
        range.selectNodeContents(el);
        var sel = window.getSelection();
        sel.removeAllRanges();
        sel.addRange(range);
    };

    function copyToClipboard(data) {
        function listener(e) {
            e.clipboardData.setData("text/html", data);
            e.clipboardData.setData("text/plain", data);
            e.preventDefault();
        }

        document.addEventListener("copy", listener);
        document.execCommand("copy");
        document.removeEventListener("copy", listener);
    };

    function copyHtmlToClipboard() {
        var dataHtmlElement = document.getElementById("data");

        dataHtmlElement.contentEditable = true;
        dataHtmlElement.readOnly = false;

        selectElementContents(dataHtmlElement);
        copyToClipboard(dataHtmlElement.innerHTML);

        dataHtmlElement.contentEditable = false;
        dataHtmlElement.readOnly = true;

        window.getSelection().removeAllRanges();
    };


    var button = $("button")
    button.on("click", copyHtmlToClipboard)

});

Upvotes: 1

Related Questions