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)?

Raygun Pulse

Recent Features

  • By
    5 HTML5 APIs You Didn&#8217;t Know Existed

    When you say or read "HTML5", you half expect exotic dancers and unicorns to walk into the room to the tune of "I'm Sexy and I Know It."  Can you blame us though?  We watched the fundamental APIs stagnate for so long that a basic feature...

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

    David asked me if I'd be up for a guest post picking out some of my favorite Pens from CodePen. A daunting task! There are so many! I managed to pick a few though that have blown me away over the past few months. If you...

Incredible Demos

  • By
    Optimize Your Links For Print Using CSS â Show The URL

    When moving around from page to page in your trusty browser, you get the benefit of hovering over links and viewing the link's target URL in the status bar. When it comes to page printouts, however, this obviously isn't an option. Most website printouts...

  • By
    Create a Dynamic Flickr Image Search with the Dojo Toolkit

    The Dojo Toolkit is a treasure chest of great JavaScript classes.  You can find basic JavaScript functionality classes for AJAX, node manipulation, animations, and the like within Dojo.  You can find elegant, functional UI widgets like DropDown Menus, tabbed interfaces, and form element replacements within...

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!