Grab selected text in Iframe and highlight it using Rangy


I’ve been recently working on some project wherein I wanted to grab the selected text and highlight it. The best approach in my opinion would be put markers around selected text and calculate their position starting from <body> tag.

Failed Attempt

In this approach, I decided to take advantage of Range Object to grab selected text and wrap it in markers in order to highlight it. This works pretty well until we select texts from different ranges. This is the code I’ve used, Notice I’m wrapping the selected text by <chucknorris> tag which acts as a marker in this case but unfortunately range.surroundContents() method does not allow to wrap texts of different ranges and returns an error.

Uncaught Error: BAD_BOUNDARYPOINTS_ERR: DOM Range Exception 1

function grabSelectedText() {
  var t = '',
  window = $('#iframe')[0].contentWindow.window,
  document = $('#iframe')[0].contentWindow.document,
  chucknorris = document.createElement("chucknorris");

  if (window.getSelection) {
    t = window.getSelection();
  } else if (document.getSelection) {
    t = document.getSelection();
  } else if (document.selection) {
    t = document.selection.createRange().text;
  }

  var range = t.getRangeAt(0).cloneRange();
  range.surroundContents(chucknorris); // Does not work for different ranges
  t.removeAllRanges();
  t.addRange(range);

  return t;
}

Range differs when selected texts belong to different HTML tags. For example, If we select and wrap Bold Italic text from <strong>Bold </strong><em>Italic</em> string, we’ll have 2 different ranges because both the words wrapped inside different tags but for individual words, it will work.
Demo (keep your Firebug console open to see errors)

Successful Attempt

After googling a lot, I’d come across Rangy – an awesome cross-browser range and selection library in Javascript. It provides a simple standards-based API for performing common DOM Range and Selection tasks in all major browsers. So, above function changes to below:

getSelectedText: function () {
  var window = $('#iframe')[0].contentWindow.window, 
    selection = rangy.getSelection($('#iframe')[0]);

  markerApplier = rangy.createCssClassApplier(null, {
    elementTagName: 'chucknorris'
  });
  markerApplier.toggleSelection(window);

  return selection; 
}

Usage

I’ve written a wrapper named Rangee on top of Rangy to simply grab and highlight the selected text.

  // For Iframe's content and Storing the selection details in localStorage
  var objSel = rangee.init('iframe', iframe.contentWindow.window || iframe.contentDocument.defaultView).grabSelection();
  selection1Storage.push(objSel);
  localStorage['selection1'] = JSON.stringify(selection1Storage);  

  // For parent window and Storing the selection details in localStorage
  var objSel = rangee.init('div', window).grabSelection();
  selection2Storage.push(objSel);
  localStorage['selection2'] = JSON.stringify(selection2Storage);

  // Highlighting previous selection (stored in localStorage)
  rangee.init('iframe', iframe.contentWindow.window || iframe.contentDocument.defaultView);
  rangee.highlight(JSON.parse(localStorage['selection1']) );
  rangee.init('div', window);
  rangee.highlight(JSON.parse(localStorage['selection2']) );

Final Demo

View Demo

References

http://code.google.com/p/rangy/
http://stackoverflow.com/q/11537032/415057

If you found this article useful in anyway, feel free to donate me and receive my dilettante painting as a token of appreciation for your donation.

4 thoughts on “Grab selected text in Iframe and highlight it using Rangy

  1. Possible bug? http://imgur.com/p8N6X, You can easily make out what I selected, as it is highlighted in yellow, but it’s highlighting some more text on it’s own, in grey. This is an edge case, since it happens only when I select differently formatted text, in this case two words, “some of”. Tested on Google Chrome Version 21.0.1180.79 on OS X Mountain Lion

  2. Hey greenmango,

    Thanks for trying out the demo.

    Its not a bug though. The selection remains at the same position and that effect is seen when an extra markup (span tags) around the selection is added. One possible way to get rid of that issue is to hide the selection after highlighting.

  3. Classapplier doesn’t work if we zoom in or zoom out the text. I am trying to create a epub reader and used same methodology to implement highlight function, but on zooming in or out the highlight marker is getting displaces.
    Any suggestions?

    • I think on zoom scale, you get the zoom ratio or value which you can use to multiple it with the actual marker’s position to move it to the right place.

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.