Grow your CSS skills. Land your dream job.

Highlight Certain Number of Characters

Published by Chris Coyier

I had a unique programming challenge the other day that I thought I'd share here. It's rather specific and probably won't be of huge general use, but hey, it might be interesting.

The idea was to have 160 characters become "selected" when you click on any letter in a block of text:

I didn't think it was possible to "actually" select the text automatically like that, since that seemed to me kind of like an operating-system level function. I really don't know though, correct me if I'm wrong. So instead what I set out to do what "fake" it, by applying a background color behind the appropriate characters. In the context of what we were doing, this worked fine anyway. Then those same characters were moved down into a text box for potential editing and submission.

The hardest part, to me, was thinking of a way to figure out exactly what character in a string of text was clicked on. So again, I kind of cheated. I figured if I wrapped every single character in a <span> I could watch for click events on each of those spans. Just as good. jQuery as usual:

var theText = $("#theText");

var theString = theText.text();

var numCharacters = theString.length;

var newHTML = "";

for (i = 0; i <= numCharacters; i++) {
    
    var newHTML = newHTML + "" + theString[i] + "";

}

theText.html(newHTML);

Now I bound (binded?) click events to each of those new span-wrapped characters. When they are clicked, the "selected" class is removed from all of them and applied to the one that is clicked. Then a for-loop fires off looping 160 times. It moves to the next character and highlights it (by adding a class):

$("span").click(function(){

    $("span").removeClass("selected");

    $(this).addClass("selected");
    
    var nextSpan = $(this);
    
    for (i = 1; i <= 160; i++) {
    
        nextSpan = nextSpan.next();
    
        nextSpan.addClass("selected");
                    
    }

});

Because I also wanted to move this newly-selected text down into a text box (for potential further editing), I run a second loop inside the click function. This second loop loops through each character that is currently selected and appends it to a string being held in a jQuery data chunk. At the end of the loop, that data chunk is applied to the textarea:

$("#result").data("result", "");
        
$(".selected").each(function() {

    var oldResults = $("#result").data("result");
            
    var newResults = oldResults + $(this).text();
    
    $("#result").data("result", newResults);

});

$("#result").val($("#result").data("result"));

Because the goal of this was to ultimately submit the characters to another URL, I made a button which would do that. When that button was clicked, it took the value of the text area, appended it to a URL and sent it away:

$("#sendit").click(function() {

    var toURL = "?=" + $("#result").val();

    window.location = toURL;
    
    return false;

});

So again, rather specific and I'm sure not widely useful, but I had never seen any other functionality like this so perhaps it'll be useful to someone in a similar spot.

 

View Demo   Download Files

 

UPDATE: Just as I hoped, someone jumped in with another (smarter) approach: Matt Wondra has a demo available here. It makes use of a textarea and JavaScript "ranges". The only weakness being textareas inherent dumbness of not being able to grow in height with content.

Comments

  1. Chris
    Permalink to comment#

    Very nice little trick. I like. I’m tellin ya man. jquery-tricks.com. The future. =)

  2. will be helpful for some future projects,.. thanks

  3. My first thought when I saw the title was “Why 160 characters? Twitter is limited to 140.” Although your post has nothing to do with twitter, it gave an idea.

    I just created a Firefox add-on (ReplacURLr) that replaces the text in the location bar with a shortened URL, for the purpose of tweeting via TwitterBar. I think I might add the option of copying 140 characters of text to the bar. Thanks for the inspiration!

    • Chris
      Permalink to comment#

      You’re right about Twitter and it’s 140 character limit but a text message is usually 160 and Twitter allows only 140 so they can add a name to it. So really it is like 160.

    • mrtom
      Permalink to comment#

      Hi Chris.

      I’m not sure if this will be important for you or not, but a text message is not always 160 characters. It’s 160 standard characters, but plenty of plain ASCII characters are represented in the GSM character set as double characters – things you might use quite a bit unfortunately, like €, [, ], {,}, ^.

      There’s a full list here: http://www.mxtelecom.com/us/tech/knowledge_base.jsp?faqid=5#question7.

      I can probably help you out if this is important for you, just drop me a line.

      Tom

  4. This would have been even better it you had placed near the approve it button a copy to clipboard button.

  5. Permalink to comment#

    My first instinct was that you ought to be able to get javascript to figure out the length of the text block, and help you out without wrapping every character in a span – but now that I think of it, I dont think that’s possible (how could javascript relate the cursor position to the character number? Anybody?).

    Also, you’d have to run some pretty fancy stuff to get only PART of the selected block to highlight. As much as I want to say there’s a better way, I think you’ve hit the nail on the head here.

  6. Now you’ve made me curious, Chris. I know there’s an easier way to do this but at least your way works :)

  7. This sounds like a really fun and challenging problem. Your solution is really nice, but like Eric I’m eager to see if there’s a better way…

    One of the biggest things that makes me queasy is that this breaks “copy” functionality — the usability purist in me says that if it looks like the text is selected, ctrl/cmd+c should work.

    I wonder (just thinking off the top of my head) — did you consider putting the text in a textarea element? I’m pretty sure there is some JavaScript functionality for current cursor position and the like, and you could always style it to not look like a textarea. Of course if you’re a real purist you’ll wrestle with the un-semantic-ness of that, but I’m not sure that character spans are much better…

    Anyways, just curious. If you haven’t tried this (or already rule it out as unfeasible) I might take a crack at it later today.

    Great work as always!

  8. Permalink to comment#

    Nice truck indeed.

    However, it doesn’t work by selecting text the ‘normal‘ way: clicking and dragging, which is I think the way most non-technical users would select text anyway.

    Cool trick nonetheless.

    Thanks.

  9. Noah Hendrix
    Permalink to comment#

    I wonder if there is a place for the jQuery wrap function to automatically wrap the characters, but I feel like possibly a solution lies in the JS boxes on many coding websites that have “select all” options ala snipplr.com.

  10. terrence
    Permalink to comment#

    maybe we need to start putting text in “textarea” instead of “p”

    just a thought

  11. You use Javascript’s Selection API to grab selected ranges, extend the range, and so forth. Here it is for Mozilla:

    MDC Selection

  12. I’ve never actually used ranges but it should be possible to do this with the “actual” selection by making use of ranges and adding them to the selection.

    https://developer.mozilla.org/en/DOM/Selection

    https://developer.mozilla.org/en/DOM/range

  13. http://christophdum.com/syntaxhighligher/grabber019.htm
    (click on the text and see a cursor + select text –> see what happens (it kinda works, although it’s quite buggy (for example: only select text within a paragraph, because it removes all HTML within the selection)))

    i’ve been playing around with JS-text-selection for a little while (i’m eventually trying to create a text-area replacement (and maybe sometime create a nice little JS wysiwyg editor or syntax-highlighter))

    anyway, wrapping every single char was my first idea also, but that sucks

    using ranges, does not add any unnecessary HTML, and it sets the cursor EXACTLY where you want it (if you click the left of the char, the cursor will be put to the left / if you click the right side, the cursor is gonna be inserted after the char (just like in a real text-editor))

    PS: for all you usability guys out there: if a text is selected, you could copy that text into a textbox which is not visible for the user and .focus() it so that the user can copy it

    PPS: this is just a fun-project of mine – the code is ugly and buggy – it’s just a prove of concept (i usually write good code and use jquery ;) )

  14. @Sean + @ddunlop – The issue is more about finding the exact character where the user clicked. If you already know what range you want, using Selection and range are easy. That’s why Chris had to put each character in a span – so the javascript would know which letter was clicked on.

    In other news — I did it with textarea! :D

    See the demo at http://mattwondradesign.com/tests/highlightchars/.

    The biggest misfortune of using textareas is that they aren’t vertically flexible, so you’d need to add another javascript hook to resize the textarea to the content. But it allows for ctrl/cmd-c and doesn’t require spans to find the click!

    Not quite sure if you’d call it an improvement, but maybe just another way to do it.

    • Nice! DEFINITELY an improvement. That is a bummer about textareas not being able to dynamically resize. That is a bit of an issues on HTML-Ipsum: http://html-ipsum.com/

      I’ll update the article to point to your better solution as well.

    • Excellent. Implemented it:

      http://html-ipsum.com

    • When you click on a character, the window selection’s anchor is set to the character you clicked on. So this simple function ( + mootools addEvent) would select the next 140 characters or the rest of the node.

      window.addEvent('domready',function() {
      $$('p').addEvent('click',function(e) {
      var s = window.getSelection();
      s.extend(s.anchorNode, s.anchorOffset + 140 < s.anchorNode.length ? s.anchorOffset + 140 : s.anchorNode.length);
      });

      });

      With just a little bit of more work, you could store the extra length and then add that many characters from the next node to the selection as well.

      The Selection API really does make such a task super simple :)

    • When you click on a character, the window selection’s anchor is set to the character you clicked on.

      It would be wonderful if this worked everywhere… as far as I can tell though, unless you actually select some text (e.g. if you only click) then in Opera the anchorNode is just the whole parent node of the element that you clicked on, and anchorOffset is always 0:

      $('p').click(function(e) { // jQuery
      s = window.getSelection();
      alert(s.anchorOffset);
      });

      This is fair when you think about it: clicking doesn’t make a selection in any real sense. I haven’t tested in IE, but I don’t have high hopes…

      Which takes us back to needing to span each character again…

  15. Permalink to comment#

    Nice trick!
    Semi-Off-Topic question: how does the selected text in your site become orange? jQuery? CSS3?

    Thanks anyway :)

  16. ice
    Permalink to comment#

    use range trick is better as I think,but there is another problem with textarea,
    if the text is not plain text, but html. such as a web page’s content, there also can be another solution with range,i think.

  17. BorisCallens
    Permalink to comment#

    Right before I read this post, I asked this question on stackoverflow for a multi background-colored textbox: http://stackoverflow.com/questions/700433/ideas-for-multicolored-textbox.

    Your technique (and the resulting comments) gave me some inspiration to go with.

  18. Permalink to comment#

    @cant:
    I was impressed by the selected text trick too. The answer is in a previous post:

    http://css-tricks.com/overriding-the-default-text-selection-color-with-css/

    Thanks Chris, good work!

This comment thread is closed. If you have important information to share, you can always contact me.

*May or may not contain any actual "CSS" or "Tricks".