New York Times-Style Text Selection Widget Using MooTools or jQuery

By  on  

NY Times Widget

Aaron Newton made a great request to me last week: why not make my MooTools Documentation Bookmarklet function more like the New York Time's text selection widget. NYT's text selection widget listens for text selection and presents the user with a "search" icon they may click on to learn more about that term. I've tried to answer that challenge -- not in bookmarklet form but in website widget form.

The Sample HTML

<div id="content-area">
	.htaccess AJAX Apache / Server APIs Blog Bookmarking / Social Books Browsers CSS / Design Google Guest Blogger Hosting / Domain JavaScript jQuery
	link() Microsoft MooTools MySQL Optimization PHP Poll rand() Security Shell Theory / Ideas Usability / Accessibility XML / XHTML
	This blog is targeted toward all levels of web designers and developers. All web topics are discussed, including CSS, JavaScript (MooTools and jQuery), PHP, and more.
</div>

The above code is next to meaningless per the widget -- I simply want to illustrate the area I'd like it to work in has an ID of content-area.

The MooTools JavaScript

window.addEvent('domready',function(){
	(function($) {
		//gets the selected text
		var getSelection = function() {
			return $try(
				function() { return window.getSelection(); },
				function() { return document.getSelection(); },
				function() { 
			        var selection = document.selection && document.selection.createRange();
					if(selection.text) { return selection.text; }
					return false;
			      }
			) || false;
		};
		//vars 
		var url = 'https://davidwalsh.name/?s={term}', selectionImage;
		//event to listen
		$('content-area').addEvent('mouseup',function(e) {
			var selection = getSelection();
			if(selection && (selection = new String(selection).replace(/^\s+|\s+$/g,''))) {
				//ajax here { https://davidwalsh.name/text-selection-ajax }
				if(!selectionImage) {
					selectionImage = new Element('a',{
						href: url,
						opacity:0,
						id: 'selection-image',
						title: 'Click here to learn more about this term',
						target: '_blank'
					}).inject(document.body,'top');
				}
				//handle the every-time event
				//alert(selection);
				selectionImage.set('href',url.replace('{term}',encodeURI(selection))).setStyles({
					top: e.page.y - 30,	//offsets
					left: e.page.x - 13 //offsets
				}).tween('opacity',0);
			}
		});
		
		$(document.body).addEvent('mousedown',function() {
			//hider
			if(selectionImage) { selectionImage.tween('opacity',1); }
		});
		
	})(document.id);
});

During the mouseup event within the selected container (content-area), we determine if any text has been highlighted. If so, we:

  1. Insert the A element if it has not yet been injected into the body.
  2. Change the A element's URL to accommodate for the new search term.
  3. Position the element to at an offset position above where the mouse goes up.

During every mousedown event we hide the A element.

The jQuery JavaScript

/* attempt to find a text selection */
function getSelected() {
	if(window.getSelection) { return window.getSelection(); }
	else if(document.getSelection) { return document.getSelection(); }
	else {
		var selection = document.selection && document.selection.createRange();
		if(selection.text) { return selection.text; }
		return false;
	}
	return false;
}
/* create sniffer */
$(document).ready(function() {
	var url = 'https://davidwalsh.name/?s={term}', selectionImage;
	$('#content-area').mouseup(function(e) {
		var selection = getSelected();
		if(selection && (selection = new String(selection).replace(/^\s+|\s+$/g,''))) {
			//ajax here { https://davidwalsh.name/text-selection-ajax }
			if(!selectionImage) {
				selectionImage = $('<a>').attr({
					href: url, 
					title: 'Click here to learn more about this term',
					target: '_blank',
					id: 'selection-image'
				}).hide();
				$(document.body).append(selectionImage);
			}
			selectionImage.attr('href',url.replace('{term}',encodeURI(selection))).css({
				top: e.pageY - 30,	//offsets
				left: e.pageX - 13 //offsets
			}).fadeIn();
		}
	});
	$(document.body).mousedown(function() {
		if(selectionImage) { selectionImage.fadeOut(); }
	});
});

Follows the same principal as above.

Ideas & Enhancements

  • Depending on your philosophy, you may want to implement a minimum character check as well:

    if(selection && (selection = new String(selection).replace(/^\s+|\s+$/g,'')) && selection.length > 4) { //5 char min
    
  • You may want to also fidget with adding/modifying text selection with keyboard keys as well. I've chosen to pass on that.
  • Saving the selection content via AJAX for analytical reasons may not be a bad idea either.

Have any other ideas for improvement? Would you have any use for this on your website(s)?

Recent Features

  • By
    Designing for Simplicity

    Before we get started, it's worth me spending a brief moment introducing myself to you. My name is Mark (or @integralist if Twitter happens to be your communication tool of choice) and I currently work for BBC News in London England as a principal engineer/tech...

  • By
    Create Namespaced Classes with MooTools

    MooTools has always gotten a bit of grief for not inherently using and standardizing namespaced-based JavaScript classes like the Dojo Toolkit does.  Many developers create their classes as globals which is generally frowned up.  I mostly disagree with that stance, but each to their own.  In any event...

Incredible Demos

  • By
    MooTools &#038; Printing &#8211; Creating a Links Table of Contents

    One detail we sometimes forget when considering print for websites is that the user cannot see the URLs of links when the page prints. While showing link URLs isn't always important, some websites could greatly benefit from doing so. This tutorial will show you...

  • By
    JavaScript Canvas Image Conversion

    At last week's Mozilla WebDev Offsite, we all spent half of the last day hacking on our future Mozilla Marketplace app. One mobile app that recently got a lot of attention was Instagram, which sold to Facebook for the bat shit crazy price of one...

Discussion

  1. This is totally cool, and I KNOW this would be a great feature to add to a hyperlocal blog that I work for.

  2. Nice use! Could be a little neater on the MooTools side though…

    if(!selectionImage) { 
    	selectionImage = new Element('a', { 
    		'href': url.substitute({
    			'term': encodeURI(selection)
    		}), 
    		'id': 'selection-image', 
    		'title': 'Click here to learn more about this term', 
    		'target': '_blank', 
    		'fade': {
    			'duration': 50
    		},
    		'styles': { 
    			'top': e.page.y - 30,
    			'left': e.page.x - 13
    			'opacity':0, 
    		}
    	}).inject(document.body, 'top'); 
    }
  3. Rich

    Nice man. very cool and you got the jquery version which makes it so much better for me. I might have to try this on a project. thanks again for all the work you put into your blog. Its much appreciated.

  4. Mr.X.

    Very nice thanks!

  5. I am with a PHP web development company http://www.rightwaysolution.com
    Very nice article thanks for explaining this. i am bookmarking this and will surely return for more knowledge:)

  6. Pretty sweet, always enjoy your blog.

  7. nice article dev….:)

  8. Is the plugin you’re using on this website?

  9. Yep — it was really easy to implement too!

  10. The mootools demo isn’t working for me (the jqery one does).

  11. @Graeme Coultrip: Updated!

  12. @David Walsh: It was simple opacity mistake.

  13. Cool. I think this could be very useful.

  14. Willian

    Hi,

    How can I change getSelected function to return the start and end position index?

  15. hi devid.

    i am following Your blog for Years.

    first of all , i appreciate you for your knowledge sharing ;)

    i am using Joomla 1.5 in my New Project and as you may know, Joomla Is using Mootools 1.11 in its core.

    So by know , i want to use this Useful plug-in and it does not work with this mootools version.

    how do you see this problem? any solution?

    i had updated the Joomla Mootools version, but some other part of the website drops :D

    best Regards,

    M.Rashidi

  16. That’s really wow I say because this would be a source of getting inspiration and that is all what is required to start the things, thank you.

  17. The mootools demo isn’t working for me, too. It bothers me a lot. Who can help?

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!