Custom select function with copy to clipboard pure

2019-04-05 05:33发布

问题:

The current code appends a button to quickly select some code in a <pre> tag. What I want to add is the ability to copy that content to the clipboard and change the button text to "copied".

How can I achieve it by modifying the current working code below? I wouldn't mind using clipboard.js, jQuery bits or just native JS support as it was introduced since Chrome 43. I just dont know how to go on from here on adding what I need.

function selectPre(e) {
    if (window.getSelection) {
        var s = window.getSelection();
        if (s.setBaseAndExtent) {
            s.setBaseAndExtent(e, 0, e, e.innerText.length - 1);
        }
        else {
            var r = document.createRange();
            r.setStart(e.firstChild, 0);
            r.setEnd(e.lastChild, e.lastChild.textContent.length);
            s.removeAllRanges();
            s.addRange(r);
        }
    }
    else if (document.getSelection) {
        var s = document.getSelection();
        var r = document.createRange();
        r.selectNodeContents(e);
        s.removeAllRanges();
        s.addRange(r);
    }
    else if (document.selection) {
        var r = document.body.createTextRange();
        r.moveToElementText(e);
        r.select();
    }
}
var diff = document.getElementById('diff_table').getElementsByTagName('tr');
var difflen = diff.length;
for(i=0; i<difflen; i++) {
    var newdiff = diff[i].childNodes[1];
    if (newdiff.className && (newdiff.className == 'added' || newdiff.className == 'modified')) {
        newdiff.className += ' diff-select';
        newdiff.innerHTML = '<div class="btnbox"><button class="btn btn-default btn-xs" onclick="selectPre(this.parentNode.nextSibling)">Select</button></div>' + newdiff.innerHTML;
    }
} 

回答1:

For some reason indeed your selectPre function is not found when reproducing the case on jsfiddle. Jsfiddle may get rid of what it thinks is dead code or rename it for the sake of minification.

But if what it does is selecting the content of a <pre> tag, the clipboard.js library (that you are ready to use) can do that already on its own.

So you end up by requiring a correct configuration of the Clipboard object. Using that one:

new Clipboard('.btn', {
    // The targeting to the correct content is done here.
    target: function(trigger) {
        return trigger.parentNode.nextSibling;
    }
    // clipboard.js will take the entire inner content of the <pre>,
    // I think this is what you are trying to do in your "selectPre"
    // function, but I am not sure.
});

it mimics your selectPre(this.parentNode.nextSibling) that you no longer need to attach to the onclick attribute of your button.

Demo: http://jsfiddle.net/5k60nm1y/

Note that I had to guess what your table structure is. It might differ from your actual table so you may need to fine-tune how newdiff is assigned to the correct cell.

If you need something more complicated than just the inner content of the <pre> tag, you could fine-tune the behaviour of the Clipboard object by passing a custom function to the text property of the Clipboard constructor option, instead of using the target property. Check the clipboard homepage, it is quite self-explanatory.

As mentioned by Zac, you would have made people's task easier (and you would probably have received a solution much faster) if you could have shared your HTML table. I would not have needed to guess and create a fake one. Furthermore, the code I would have provided you with would have been directly applicable to your real table, whereas now it may still need customization. Hopefully I guessed it right enough and my table is close to yours.



回答2:

I applied a piece of code from this resource, How do I copy to the clipboard in JavaScript?, to your code, for you to easily see how it can be done.

I also modified your onclick="selectPre(...)" to this onclick="selectPre(this)" and added a couple of variables in the "selectPre" function.

Here is also a Fiddle demo

function selectPre(b) {
    var s;                               // added - selection variable
    var e = b.parentNode.nextSibling;    // added - parent sibling element
    if (window.getSelection) {
        var s = window.getSelection();
        if (s.setBaseAndExtent) {
            s.setBaseAndExtent(e, 0, e, e.innerText.length - 1);
        }
        else {
            var r = document.createRange();
            r.setStart(e.firstChild, 0);
            r.setEnd(e.lastChild, e.lastChild.textContent.length);
            s.removeAllRanges();
            s.addRange(r);
        }
    }
    else if (document.getSelection) {
        var s = document.getSelection();
        var r = document.createRange();
        r.selectNodeContents(e);
        s.removeAllRanges();
        s.addRange(r);
    }
    else if (document.selection) {
        var s = document.body.createTextRange();
        s.moveToElementText(e);
        s.select();
    }

    // added - copy and change button text
    if (s) {
        try {
            var successful = document.execCommand('copy');
            // var msg = successful ? 'successful' : 'unsuccessful';
            // console.log('Copying text command was ' + msg);
            if (successful) {
                b.innerHTML = "Copied";
            }
        } catch (err) {
            // console.log('Oops, unable to copy');
        }
    }
}
var diff = document.getElementById('diff_table').getElementsByTagName('tr');
var difflen = diff.length;
for(i=0; i<difflen; i++) {
    var newdiff = diff[i].childNodes[1];
    if (newdiff.className && (newdiff.className == 'added' || newdiff.className == 'modified')) {
        newdiff.className += ' diff-select';

        // altered - onclick handler
        newdiff.innerHTML = '<div class="btnbox"><button class="btn btn-default btn-xs" onclick="selectPre(this)">Select</button></div>' + newdiff.innerHTML;
    }
}