13

I have a div which contains a series of span tags, each containing a string of text. I'd like to attach a jQuery click event to all of the spans so that when the text inside any span is clicked, the entire line of text (dom > innerText object) will be auto selected to facilitate the drag/drop or copy/paste of the text string.

For example, my content is...

<div id="mySpans">
  <span>&nbsp;This is my text&nbsp;</span>
  <span>&nbsp;This is my text&nbsp;</span>
</div>

If the cursor is clicked on any text inside a span, I want to select the text within that span, so that it can be drag/dropped (without the span tags, just the innerText of the span) as a copy.

Does jQuery have an easy means to do this?

EDIT: A more detailed explanation of what I'm trying to accomplish:

Without aid of script, in order to copy a block of text, the user has to manually drag select a selection rectangle across the text block. The text then becomes selected signaling that a click & drag event will pick up all of the selected text. So I'm trying to create script that allows a single click on the text to automatically select the text for the user so they don't have to manually do it themselves.

Scott B
  • 38,833
  • 65
  • 160
  • 266
  • I think, for security reasons it is not allowed to manipulate the users selection. That would be pretty evil. It would be something like if someone says "Hi", you would make him say "I want a pizza", accept it as an order for a pizza and let him pay for it... – Justus Romijn Nov 12 '10 at 14:07
  • @Justin, perhaps, but I'm just making it simpler for the user to select a block of text in this case. The script is just being used as a shortcut means of allowing a click event (that they initiate) to complete a selection (which many users find hard to do). – Scott B Nov 12 '10 at 14:20
  • @Justus, yeah, but you'd do that on the server-side anyway, letting the client *see* you screw with his order would be the lower end of the 'evil' scale (and the higher end of the 'stupid' scale)... =b – David Thomas Nov 13 '10 at 12:51
  • one may simply triple-click to automatically select the whole contents of a span, and a triple-click-drag results in selecting multiple span contents ;) – RozzA Jun 16 '13 at 09:46

8 Answers8

16

Right. The short answer is: you can't.

That, however, isn't really very helpful. So, if you're prepared to accept a slightly hacky approach, with at least one caveat, you can:

Given the html:

<div id="wrap">
    <span class="copyText">This is some text to copy.</span>
    <span>Can't copy <em>this</em> (automatically...)!</span>
    <span class="copyText">And this is yet more text.</span>
</div>

And the CSS:

span.copyText {
    position: relative;
    display: block;
}
textarea {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    border: 0 none transparent;
    margin: 0;
    padding: 0;
    outline: none;
    resize: none;
    overflow: hidden;
    font-family: inherit;
    font-size: 1em;
}

You can use the following jQuery:

$(document).ready(
    function() {
        $('.copyText').click(
            function() {
                if ($('#tmp').length) {
                    $('#tmp').remove();
                }
                var clickText = $(this).text();
                $('<textarea id="tmp" />')
                    .appendTo($(this))
                    .val(clickText)
                    .focus()
                    .select();
        return false;
    });
$(':not(.copyText)').click(
    function(){
        $('#tmp').remove();
    });

});

With the requisite JS Fiddle demo, at: http://jsfiddle.net/davidThomas/ZmYBh/.

The main caveat is that the element you want to copy cannot (with this approach at least) wrap from one line to the next (unless it's also display: block), I suspect it has something to do with how native form elements are rendered as 'solid' rectangles, unlike most other inline elements, such as html which form a more...'fluid'(?) rectangle when wrapping).

There may be others, however.

JS Fiddle demo to show that it does work with wrapping text, so long as the parent container element (span) is still display: block;.


Edited: to add that I tried using inputs instead of textarea which, predictably, failed to work any differently/better than textarea, and also <span contenteditable>, which (again, predictably) didn't select the text at all, but did insert the caret at the beginning of the text.

Sigh. I think there should be a far easier answer to this question, but I can't see it for the life of me.

David Thomas
  • 249,100
  • 51
  • 377
  • 410
9

use DOM ranges: http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html

var span = ...
var range = document.createRange();
range.setStartBefore(span.firstChild);
range.setEndAfter(span.lastChild);
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
Evan
  • 91
  • 1
  • 1
5

Found this core-javascript solution that works well and is hack-free: http://coderzone.org/library/Select-text-in-a-DIV-SPAN-or-table-cell_1047.htm

I took the liberty to change the code a bit so that it accepts the element node as argument instead of an element id.

// Selects text inside an element node.
function selectElementText(el) {
    removeTextSelections();
    if (document.selection) {
        var range = document.body.createTextRange();
        range.moveToElementText(el);
        range.select();
    }
    else if (window.getSelection) {
        var range = document.createRange();
        range.selectNode(el);
        window.getSelection().addRange(range);
    }
}

// Deselects all text in the page.
function removeTextSelections() {
    if (document.selection) document.selection.empty(); 
    else if (window.getSelection) window.getSelection().removeAllRanges();
}
Phillippe Santana
  • 2,906
  • 2
  • 28
  • 29
1

I wanted to do something similar. My website has quotes that I wanted users to be able to easily copy. I also wanted to add the author's name to the string.

Normally, I have user-select set to none, so I created a class that is programmatically applied when needed...

.editable {
    user-select: text;
    -webkit-user-select: text;
    -moz-user-select: text;
    -ms-user-select: text;
    -o-user-select: text;
}

All of the quotes are in a class called "paragraph_quote". When a site visitor clicks on a quote, the follow sequence occurs:

$(".paragraph_quote").on("click", function() {
    var addendum = " [by Author]";

    if (! $(this).attr("contenteditable") || $(this).html().indexOf(addendum) === -1) {     
            $(this).removeData("quote")
            .data("quote", $(this).html())
            .html($(this).html() + addendum)
            .attr("contenteditable", true)
            .addClass("editable")
            .focus()
        ;
    }
    document.execCommand('selectAll', false, null);
}).on("blur", function() {
    $(this).removeClass("editable").html($(this).data("quote"));
});
  1. Look to see if the following steps have already been executed (i.e., whether the user is clicking for a second time inside the same paragraph). If it's the first time, execute steps 2 through 7. If not, just step 8.

  2. Remove any data that might have been stored if this isn't the first time that quote was clicked.

  3. Store the quote's HTML as data. This allow you to modify the HTML for the copy (if you would like) and then easily return it to its original state.

  4. Add any additional text (e.g., the author's name) to the HTML. Not shown below: I also use .replace() to remove any special HTML characters like non-breaking spaces, em-dashes, etc.

  5. Make the paragraph editable.

  6. Add the editable class.

  7. Set the focus to the paragraph. The editable class ensures the appearance of the focus outline.

  8. Select the entire content of the editable paragraph. Not that this step is useful, even if the other steps have already been taken, because it causes the entire selection to be re-highlighted if the user clicks within the selection.

  9. When the user clicks outside of the paragraph, remove the editable class and...

  10. Restore the HTML to its previous state (if you modified as indicated in step 4, above).

Alan M.
  • 1,309
  • 2
  • 19
  • 29
1

tbleckert is on the right track. The .select() event is only available for inputs though, so you'd need to have your <span> become an input and then style it with no background, no border, and no focus ring. Then, you can do this:

<input type="text" style="border:none; background:transparent; outline: none;" class="selectOnClick" />

and then your jQuery would look something like this

$('input.selectOnClick').click(function(){ $(this).select(); });

This is untested code but should point you in the right direction.

jxpx777
  • 3,632
  • 4
  • 27
  • 43
  • Since this code resides in a plugin that resides in the WordPress editor, and is enclosed in a parent form tag, will the appearance of the input elements alter the post event when a post is saved or published? I don't want to add noise to the submit in that case. – Scott B Nov 12 '10 at 14:33
  • Appearance shouldn't matter and the field should still be editable and all that. – jxpx777 Nov 12 '10 at 18:10
  • I don't want the fields to be editable. They are read only and just for copying/pasting or drag/drop into the content. Does jQuery have a method for text selection on an element's innerText? That's what I'm looking for here. – Scott B Nov 12 '10 at 20:12
  • I don't believe jQuery has that method. But, you can disable the inputs or make them readonly (depending on your needs) and achieve the same thing. – jxpx777 Nov 13 '10 at 16:20
  • That being said, here's [this post](http://stackoverflow.com/questions/985272/jquery-selecting-text-in-an-element-akin-to-highlighting-with-your-mouse/2838358#2838358) on SO as well that purports to solve the problem. I haven't tested it though. – jxpx777 Nov 13 '10 at 16:21
0

Check the select() event :)

tbleckert
  • 3,763
  • 4
  • 32
  • 39
  • Well, at the time I was new here, it was like two years ago..Give me a break :P I totally agree with you though – tbleckert Aug 18 '12 at 22:29
0
$("span").click(function(){
var mytext  = $(this).text()
})

will put the text in a javascript variable ... but it may be quicker to have a look at jQuery Ui, specifically draggable.

NimChimpsky
  • 46,453
  • 60
  • 198
  • 311
  • I think he just wants the text to be highlighted so that the text can be copied via the context menu. I'm not sure that he wants it assigned to a JavaScript variable. – David Thomas Nov 12 '10 at 13:39
  • @David: Yes, copied via context menu or drag/dropped into content. If text is selected, its usually automatically draggable. And since the target is the WordPress content editor, the drop zone is already prepped for the drag/drop. – Scott B Nov 12 '10 at 14:22
  • @David: I think you have the best understanding of what I'm trying to do. Any suggestions? – Scott B Nov 12 '10 at 14:23
  • @Scott B, I understand the question, I just can't find any way of selecting (non-`input`/`textarea`) text automatically via jQuery/JavaScript. – David Thomas Nov 12 '10 at 22:27
  • @Scott B, see my answer (elsewhere on the page) for the best I could come up with. – David Thomas Nov 13 '10 at 12:52
0

Try something like

$('#mySpans span').hover(function() {
    $(this).addClass('spanhover');
}, function() {
    $(this).removeClass('spanhover');
});

where you dynamically add a class "spanhover" you defined in your CSS section like

#mySpans.spanhover {background-color:yellow;}
initall
  • 2,385
  • 19
  • 27
  • I think he wants it to be available as a text-selection to the context menu (or whatever) for copying to the clip-board; I don't think he's trying to style it. – David Thomas Nov 12 '10 at 13:41
  • @David: but he writes "... the text will be ... highlighted...". The hidden second part of the question, as @NimChimpsky pointed out, could then be done with 'draggable' ui code. – initall Nov 12 '10 at 13:53
  • David is correct. Bad choice of words on my part. In this context, I mean selected, not styled. – Scott B Nov 12 '10 at 14:10