7

I understand the security reasons behind dis-allowing JS copying arbitrary text to the clipboard, but is there a method by which clicking a button can select the text in a pre node similar to how the select() function works in an input?

I am not looking for the jQuery plugin that copies to clipboard. I just want to text in pre block to be highlighted so user can ctrl-c to copy.

I seem to finding squat.

Alice Wonder
  • 896
  • 2
  • 9
  • 17

6 Answers6

14

This is what you need:

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

And the html:

<pre onclick="clip(this);" id="copy_paste"></pre>

Or if you want to programmatically do this:

clip(document.getElementById("copy_paste"));

Or in jquery:

clip($("#copy_paste")[0]);

You cannot pass the jquery element clip() as argument el. It will give "TypeError: Argument 1 of Range.selectNodeContents does not implement interface Node."

Peter Lada
  • 383
  • 3
  • 12
7

It's not too hard. You need separate branches for IE < 9 and all other browsers. Here's a function to select the contents of an element:

Live demo: http://jsfiddle.net/yQa2w/

Code:

function selectElementContents(el) {
    if (window.getSelection && document.createRange) {
        // IE 9 and non-IE
        var range = document.createRange();
        range.selectNodeContents(el);
        var sel = window.getSelection();
        sel.removeAllRanges();
        sel.addRange(range);
    } else if (document.body.createTextRange) {
        // IE < 9
        var textRange = document.body.createTextRange();
        textRange.moveToElementText(el);
        textRange.select();
    }
}
Tim Down
  • 318,141
  • 75
  • 454
  • 536
  • Nice solution. Had no idea this option existed and prefer it to the conversion into text area, especially since I have data-binding on my
     tag with AngularJs. Good work!
    – Lukus Feb 11 '14 at 23:28
1

take a look at

https://developer.mozilla.org/it/DOM/window.getSelection

it is not compatible with all mayor browsers, first IE implementation is in version 9

but in older IEs there is some alternative API in document.selection, that seems equivalent

http://msdn.microsoft.com/en-us/library/ms535869%28v=vs.85%29.aspx

good luck!

1

Maybe you could change the pre tag into a textarea tag when the user clicks on it:

jsFiddle: http://jsfiddle.net/WzBQf/

<button id="btnSelect">Select!</button>
<button id="btnDeselect">Deselect!</button>
<hr />
<div id="text">
    <pre id="txt1">Test</pre>
    <textarea id="txt2" readonly="readonly"></textare>
</div>

And the JavaScript (jQuery) code:

$("#txt1, #btnSelect").click(function() {
    Select();
});


function Select() {
    $("#txt2").val($("#txt1").html()).show();
    $("#txt1").hide();

    $("#txt2").focus()[0].select();
}

function Deselect() {
    $("#txt1").html($("#txt2").val()).show();
    $("#txt2").hide();
}
$("#txt2").blur(function() {
    Deselect();
});

$("#btnDeselect").click(function() {
    Deselect();
});
ComFreek
  • 29,044
  • 18
  • 104
  • 156
0

As far as I know, you can not select text outside <input> or <textarea>. Btw, why do you need that?

Oroboros102
  • 2,214
  • 1
  • 27
  • 41
  • The reason is for syntax highlighted source code displayed in a pre block. Yes, the user can highlight it and copy themselves, but that involves moving the mouse to beginning and clicking until end, and can result in frustration if they drag too far or not far enough etc. Thus - by providing a button that does the selection for them, it easier for them to copy a code example. – Alice Wonder Nov 05 '11 at 11:57
  • 1
    You can select text outside `` or ` – Tim Down Nov 06 '11 at 00:51
  • Sorry for giving wrong answer. According to Tim's link, you can use method: `window.getSelection().selectAllChildren(document.getElementById('elem_id'));` think it works only in FF. – Oroboros102 Nov 07 '11 at 08:12
0

OK - this is what I am doing based on jsFiddle's suggestion and it works. Only problem is I do not have Windows and in IE where I do have it copy to clipboard, apparently the line breaks are broken, but that's not this question (and I suspect a .replace() can fix it - I just need access to windows to play with IE):

function lineBreakCount(str){
// http://snippets.dzone.com/posts/show/5770
/* counts \n */
try {
    return((str.match(/[^\n]*\n[^\n]*/gi).length));
    } catch(e) {
    return 0;
    }
}

function nodeToCopy($node) {
// For Code Snippets - [AW]
$pre = $node.find('pre:first');
var string = $pre.text();
if (window.clipboardData && clipboardData.setData) {
    clipboardData.setData('text', string);
    } else {
    var n = 1 + lineBreakCount(string);
    $pre.attr('class','nodisplay');
    $pre.after('<textarea readonly="readonly" rows="' + n + '" class="dbg-copycode">' + string.replace(/</g,'&lt;').replace(/>/,'&gt;') + '</textarea>');
    $node.find('textarea:first').focus();
    $node.find('textarea:first').select();
    $node.find('div:last').find('button').replaceWith('<button class="dbg-copycode" type="button" title="Restore syntax highlight">Restore</button>');
    $node.find('div:last').find('button').click(function() {restorePreFromText($(this).parent().parent())});
    }
}

function restorePreFromText($node) {
// For Code Snippets - [AW]
$node.find('pre:first').removeAttr('class');
$node.find('textarea').remove();
$node.find('div:last').find('button').replaceWith('<button class="dbg-copycode" type="button" title="Select code example for copying">Select Code</button>');
$node.find('div:last').find('button').click(function() {nodeToCopy($(this).parent().parent())});
}

$(document).ready(function() {
// For Code Snippets - [AW]
var copybutton = 'Select Code';
var copytitle  = 'Select code example for copying';
if (window.clipboardData && clipboardData.setData) {
    copybutton = 'Copy Code';
    copytitle  = 'Copy code example to clipboard';
    }
$('pre[data-code]').after('<div class="dbg-rbutton"><button class="dbg-copycode" type="button" title="' + copytitle + '">' + copybutton + '</button></div>');
$('.dbg-copycode').click(function() {nodeToCopy($(this).parent().parent())});
});

The reason I'm not using .hide() where I hide the pre block is because .hide() adds a style attribute which is forbidden under the Mozilla CSP policy the server uses, so I have to set a class attribute instead.

Alice Wonder
  • 896
  • 2
  • 9
  • 17
  • It should be noted that my code relies on the pre node having the attribute data-code and (starting out pre js anyway) being the only child of a parent div node. – Alice Wonder Nov 06 '11 at 00:41