Joe Maller.com

getSelection() Workaround for Safari 1.3, 2.0 and Firefox 1.0.3

update 2: These workarounds also work with Safari 2.0 in Mac OS X v10.4 .
update: There’s a simpler fix, jump to the bottom.

Yesterday morning I noticed a change to the JavaScript/DOM getSelection() behavior in the new Safari 1.3 (in 10.3.9) and the most recent version of Firefox 1.0.3.

I’ve been using this method for years to pull selected text from web pages for several of my bookmarklets. The one I use most frequently generates a link from whatever text is selected. If nothing is selected, it grabs the document’s title. The change in getSelection() broke that bookmarklet, no selected text was recognized.

After a bit of research, I found Mozilla’s Safely accessing content DOM from chrome page which describes the security fixes behind the modification and detailing other problems the changes had caused. Based on Mozilla bug 290777 and this post by Buzz Anderson, both browsers seem to have problems with the change. Despite those bugs, I managed to find the simple workaround as described below.

What Safari and Firefox now seem to be doing is creating a DOM selection object from getSelection() instead of treating it as a simple string. The result is that getSelection() appear to be a string, but few of the string manipulation functions work without additional considerations.

The following examples are all intended to be tested as bookmarklets, drag them to your bookmarks bar for testing:

  • getSelection() test 1
    javascript:d=window.getSelection();
    alert(d);

    Works as expected in Safari 1.2.4, Safari 1.3 and Firefox 1.03, popping an alert containing the selected text. Trying to measure that returned string fails in Safari 1.3 and Firefox 1.0.3 but works in Safari 1.2.4:

  • getSelection() test 2
    javascript:d=window.getSelection();alert(d.length);

    The older version of Safari returns a character count for the string of selected text. Firefox and Safari 1.3 return “undefined”. There are quite a few other problems:

  • getSelection() test 3
    javascript:d=window.getSelection();
    alert(d.toString());

    Works in Firefox and Safari 1.2.4 but not in Safari 1.3.

  • getSelection() test 4
    javascript:d=window.getSelection();
    alert(d.toString().length);

    Getting the length after toString works in Safari 1.2.4 and Firefox.

Further inconsistencies between Safari 1.3 and Firefox 1.0.3:

  • getSelection() test 5
    javascript:d=window.getSelection();alert(d.type);

    Returns “Range” in Safari with a selection, returns “Caret” or “None” with nothing selected. Fails with “undefined” in Firefox. (I think the Firefox 1.0.3 DHTML regression bug might be preventing it from working in Firefox but I didn’t try any of the recent nightly builds.)

  • getSelection() test 6
    javascript:d=window.getSelection();
    alert(d.getRangeAt(0));

    Fails silently in Safari, returns selected text in Firefox. Safari dumps this into the Console log:
    [5956] :TypeError - Value undefined (result of expression d.getRangeAt) is not object.

  • getSelection() test 7
    javascript:d=window.getSelection();
    alert(d.typeDetail);

    Fails with “undefined” in both.

There is some good news:

  • getSelection() test 8
    javascript:d=window.getSelection();
    alert(d.isCollapsed);

    Works in both Firefox and Safari 1.3; fails in Safari 1.2.4 as “undefined”. This means (finally!) there is a workaround for my problem.

I was using the length property to determine whether a selection was empty or not, then fetching the title of the window if that value was 0. Knowing that length no longer works in Firefox and Safari, isCollapsed can be used as a conditional switch.

  • getSelection() Workaround
    javascript:d=window.getSelection();
    d=(d.isCollapsed||d.length==0)?document.title:d;
    alert(d);

    That will return any selected text or the document title if there is no selection. Tested successfully in Safari 1.2.4, Safari 1.3, Firefox 1.0.3 and presumably Safari 2.0 as well.

Line breaks were added to visible code examples because my style-sheet choked on long lines and I can’t redo the CSS right now…

Update: After working through all of the above, I realized there is a far simpler solution: +''. The Safari problem seems to be that string methods do not work on the returned object from getSelection(). Forcing the result into a string by concatenating with an empty string fixes all of my bookmarklets. Concat() fails because it’s a method of string, use the "+" joining operator and an empty string '' instead.


  • Pingback: Firefox()

  • Pingback: Backup Brain()

  • http://www.derose.net Steve

    Cool; but is there a way to get the location of the selection, instead of just the text? IE on Windows (only) provides selection.createRange(), but I haven’t found an equivalent in anything else. Am I missing something obvious?

  • http://www.joemaller.com Joe

    Steve, Firefox and Safari both have the ability to store and generate selections. The syntax of JavaScript’s Selection and Range objects differs from IE.

    The best reference I’ve found about this is the reference section of JavaScript: The Definitive Guide, I haven’t been able to find any good discussions of this online.

    This example works in Firefox but not Safari 1.3 (haven’t tried to figure out why): store and restore a text selection

    Here’s an old article about JavaScript and the DOM Range Object

  • http://www.geof.net/ Geof

    I’m working on a web annotation system using the W3C range object in Firefox. I’d hoped with the Safari update I’d be able to port it. So far, no such luck: the rangeCount() and getRangeAt() methods are not present. Unlike Firefox, Safari also immediately resets the range to a Caret when the user clicks (e.g. on a button), so even if the range were present I would need to clone the data before the click (e.g. using cloneRange()). I for one really hope they fix this.

  • possen

    In example 5, you found the getSelection().type property, how did you find that? What other properties are there. I tried printing the property of the object but it wouldn’t list them. Is there a way to get the range object from the selection, since it is reporting the type of Range?

  • http://www.joemaller.com Joe

    Danny Goodman’s Dynamic HTML (Flamingo) book, page 868. GetSelection().type appears to be a property of the Selection Object, and seems to be a Microsoft DHTML addition. It’s not listed on the Mozilla Object Reference or the W3C’s DOM Range pages.

  • possen

    Thanks for the info, I have been wanting to get a copy of that book but it seems like an update should be coming soon since it is three years old. Maybe I can borrow a copy.

  • Gerard Wassink

    The nullstring concatenation trick solved my problem nicely! Thanks for the info!

    This script, which is part of my website, allows me to show the large version of thumbnails by adding ‘?show=picture.jpg’ to its URL.

    Adding the ” + to the second line solved it!

    =====

    name = '' + document.location;
    len = name.length;
    start = 0;
    for (pos = len; pos > 0; pos--) {
    if (name.substr(pos, 6) == '?show=') {
    start = pos + 6;
    break;
    }
    }
    if (pos > 0) {
    pictname = name.substr(start, (len - start));
    document.write('');
    } else {
    document.write('<b>WRONG PARAMETER GIVEN: ');
    document.write(name);
    document.write('EXPECTED TO FIND: ?show=');
    document.write('WARN WEBMASTER PLEASE!</b>');
    }

    =====

    I hope it will not be scrambled BTW…

    Gerard

  • http://www.swedendesign.tk Henrik Feldt

    Tjena (swedish for hello)!

    Nice that you write about javascript… Anyhow… I was viewing this page in Opera and it doesn’t look that good. Half the written page is hidden.

    That was all.

    //Henke

  • Atul Kulkarni

    Need to know the parent element of window.getSelection(); for Mozilla/NS

    Like IE range.parentElement();

  • Pingback: Joe's Space()

  • http://www.lexis.com Steve Smith

    Thanks a million! I’ve been trying to figure out this Safari problem for 3 weeks (off & on). The + ” trick fixed it perfectly.

    Thanks another million!

    sas

  • Pingback: Streetmap hits 1.0 » textgoeshere()

  • Anil

    Hello Joe,
    javascript:d=window.getSelection()+”; d=(d.length==0)?document.title:d; alert(d);

    Above code did not work for me in Firefox 1.5.0.1
    Thanks,
    Anil

  • http://www.joemaller.com Joe Maller

    Check your quotes. It might be WordPress screwing up your code, but the pair after getSelection() is one single quote and one curly quote.

    I just tried the code again in Firefox 1.5.0.1 (mac) and it seems to be working fine here.

  • http://stopman.blogspot.com Michael

    In firefox 1.5 in Linux window.getSelection() didn’t work, but in firefox 1.0.6 it did work. The work around described above does work in 1.5 but make sure you have a TITLE html tag in your document otherwise it won’t. Lesson learned =).

  • Sarita

    Hey gr8 work,

    Thanks Man…

  • http://www.michaelscottweber.com M-Dub

    I’m not sure if the fix is working… I mean you guys are basically just getting the title and getSelection still doesn’t return anything (in Firefox).

    Check this out:
    https://bugzilla.mozilla.org/show_bug.cgi?id=310241

    You either have to use an event handler or be SOL.

  • http://www.michaelscottweber.com M-Dub

    Sorry to double-post, but I fixed this by calling the javascript in the onMouseDown event. That way, the selected text is still selected before the mouse-click (which de-selects it).

  • Ryan

    Doesn’t work in IE??

  • Ryan
  • ice

    window.getSelection() can’t get Selection in IFrame for Safari?
    any other ways?

  • -kinetic

    Anybody know how to get a range object from selection for safari? I want to be able to clone the range, like mentioned above, but unsure of how to go from selection to range without a getRangeAt function.

  • http://www.99114.com winderboy

    FF:
    var selection = window.getSelection();
    if(selection.selectionStart==selection.selectionEnd)
    {
    alert(“you haven’t select any text!”);
    }

  • ekbworldwide

    I ended up at thread by googling
    intitle:getSelection

    I hope somebody can answer my question.

    Using a bookmarklet I can grab a selection as text – tmp=document.getSelection(); – my question is – how can I grab a selection as html?

  • http://www.joemaller.com Joe

    @ekbworldwide I don’t think you can return HTML, but you can apparently grab the DOM nodes which can be translated back into HTML. I’ve never done it, but try this post as a place to start:

    http://groups.google.com/group/comp.lang.javascript/msg/db76484c36bde1be

  • Luke

    This is great! It works great selecting text on the page, but it won’t work when select text in a textarea. Is that possible?

  • Jim

    Very nice reading, it helped me a lot!

  • http://gadgetsfor.blogspot.com/ Gadgets For Blogspot

    any to use at my Google Chrome?

  • Kevin Martin

    Hello Guru, what entice you to post an article. This article was extremely interesting, especially since I was searching for thoughts on this subject last Thursday.

    thanks
    Facebook Application Development

  • Kevin Martin

    Good information. Thanks for these very good article. I kept on nodding with the every word you say.

    thanks
    SEO Services New York