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
    Being a Dev Dad

    I get asked loads of questions every day but I'm always surprised that they're rarely questions about code or even tech -- many of the questions I get are more about non-dev stuff like what my office is like, what software I use, and oftentimes...

  • By
    Chris Coyier&#8217;s Favorite CodePen Demos II

    Hey everyone! Before we get started, I just want to say it's damn hard to pick this few favorites on CodePen. Not because, as a co-founder of CodePen, I feel like a dad picking which kid he likes best (RUDE). But because there is just so...

Incredible Demos

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!