How to Turn Naked URLs into Hyperlinks with User Scripts

Turn Naked URLs into Hyperlinks

Make every URL clickable.

Have you ever visited a page that displayed a naked URL that you couldn't click? That is, the URL is displayed as plain text on the page, and you need to manually copy the text and paste it into a new browser window to follow the link. I run into this problem all the time while reading weblogs, because many weblog publishing systems allow readers to submit comments (including URLs) but just display the comment verbatim without checking whether the comment includes a naked URL. This hack turns all such URLs into clickable links.

The Code

This user script runs on all pages. To ensure that it does not affect URLs that are already linked, it uses an XPath query that includes not(ancestor::a). To ensure that it does affect URLs in uppercase, the XPath query also includes "contains(translate(., 'HTTP', 'http'), 'http')]".

Once we find a text node that definitely contains an unlinked URL, there could be more than one URL within it, so we need to convert all the URLs while keeping the surrounding text intact. We replace the text with an empty <span> element as a placeholder and then incrementally reinsert each non-URL text snippet and each constructed URL link.

Save the following user script as linkify.user.js:

 // ==UserScript==
 // @name    Linkify
 // @namespace   http://youngpup.net/userscripts
 // @description   Turn plain- text URLs into  hyperlinks
 // @include *
 // ==/UserScript==
 // based on code by Aaron Boodman
 // and included here with his gracious permission
 var urlRegex = /\b(https?:\/\/[^\s+\"\<\>]+)/ig;
 var snapTextElements = document.evaluate("//text()[not(ancestor::a) " + 
  "and not(ancestor::script) and not(ancestor::style) and " + 
  "contains(translate(., 'HTTP', 'http'), 'http')]", 
  document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
 for (var i = snapTextElements.snapshotLength - 1; i >= 0; i--) {
  var elmText = snapTextElements.snapshotItem(i);
  if (urlRegex.test(elmText.nodeValue)) {
   var elmSpan = document.createElement("span");
   var sURLText = elmText.nodeValue;
   elmText.parentNode.replaceChild(elmSpan, elmText);
   urlRegex.lastIndex = 0;
   for (var match = null, lastLastIndex = 0;
     (match = urlRegex.exec(sURLText)); ) { 
    elmSpan.appendChild(document.createTextNode(
    sURLText.substring(lastLastIndex, match.index))); 
    var elmLink = document.createElement("a"); 
    elmLink.setAttribute("href", match[0]); 
    elmLink.appendChild(document.createTextNode(match[0])); 
    elmSpan.appendChild(elmLink); 
    lastLastIndex = urlRegex.lastIndex;
   }
   elmSpan.appendChild(document.createTextNode(
    sURLText.substring(lastLastIndex)));
   elmSpan.normalize();
  }
 }

Running the Hack

Before installing the user script, go to http://www.mnot.net/blog/2005/05/18/WADL, an article published by Mark Nottingham on his weblog. I followed Mark's weblog for many years, and the only thing I disliked was that the links I posted in the comments section would be displayed as plain text (i.e., not as hyperlinks). The comments section at the end of this article has several contributed URLs that display as plain text, as shown in Figure 2-1.

Figure 2-1. Comments with plain-text URLs

Now, install the user script (Tools Install This User Script), and refresh http://www.mnot.net/blog/2005/05/18/WADL. All the URLs in the comments section are now real hyperlinks, as shown in Figure 2-2.

Hacking the Hack

You might want to distinguish between links that were part of the original page and links that were created by this script. You can do this by adding a custom style to the elmLink element.

Change this line:

 var elmLink = document.createElement("a");

to this:

 var elmLink = document.createElement("a");
 elmLink.title = 'linkified by Greasemonkey!';
 elmLink.style.textDecoration = 'none';
 elmLink.style.borderBottom = '1px dotted red';

The linkified URLs will now be underlined with a dotted red line, as shown in Figure 2-3.

Figure 2-2. Comments with clickable URLs

Figure 2-3. Custom styles on linkified URLs