A client wants all email addresses and urls to be clickable in an application. I would like to develop a javascript solution for this.
I found this article that shows some example code for doing this on a particular piece of text, however, fixing the links on an entire page is a little bit more involved as it would require us to only convert the URLs that are static text – and leave the URLs, already inside an <a> tag, or appearing as tag attributes, alone.
My Solution
/**
* A function that makes all static urls and email addresses in given DOM
* element's subtree to be converted to links.
*
* @param el DOM Element that we are checking
*
* This will recursively call itself until the entire subtree has been walked.
* @author Steve Hannah .
*
* Some parts were adapted from http://www.arstdesign.com/articles/autolink.html
*/
function makeLinksClickable(el){
//If no element is provided, then we will traverse the entire document.
if ( el == null ) el = document.body;
if ( el.nodeType == 3 /* text node */ ){
// We only want to replace urls in text nodes
var s = el.nodeValue;
if ( s.replace(/^\s+|\s+$/g, '').length == 0) return;
// If there is no text in this text node, we will just return and do
// nothing.
var hlink = /\b(ht|f)tp:\/\/([^ \,\;\:\!\)\(\"\'\< \>\f\n\r\t\v])+/g;
// regex for URLs
var mlink = /\b([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+\b/g;
// regex for e-mail addresses
// Copied from http://www.quirksmode.org/js/mailcheck.html
var frag = document.createElement('span');
// Because we are having difficulty putting just the A tag into
// the document after conversion, we create a wrapper <span> tag
// that will hold the resulting <a> tag. Then we will
// insert the <span> tag into the document.
// Replace URLs in this text node.
var new_s = s.replace(hlink,
function($0,$1,$2){
//s = $0.substring(1,$0.length);
// remove trailing dots, if any
s = $0;
while (s.length>0 && s.charAt(s.length-1)=='.')
s=s.substring(0,s.length-1);
// add hlink
return " " + s.link(s);
}
) ;
// Replace Email addresses in this text node.
new_s = new_s.replace(mlink,
function($0,$1,$2){
//s = $0.substring(1,$0.length);
// remove trailing dots, if any
s = $0;
while (s.length>0 && s.charAt(s.length-1)=='.')
s=s.substring(0,s.length-1);
// add hlink
return " " + s.link('mailto:'+s);
}
) ;
if ( new_s != s ){
// We only want to perform the switch if a change was made
frag.innerHTML = new_s;
var parent = el.parentNode;
parent.replaceChild(frag, el);
}
} else {
// This is not a text node, so we will loop through all of its child
// nodes and recursively change the urls and email addresses in them.
for ( var i=0, len=el.childNodes.length; i<len ; i++ ){
if ( !el.childNodes[i] ) continue;
// If for some reason this node is not set we skip it
if ( el.childNodes[i].tagName == 'A' ) continue;
// We don't want to change anything that is already inside
// an <a> tag
if ( el.childNodes[i].tagName == 'SCRIPT' ) continue;
// We leave scripts alone because we don't want to screw up
// any javascripts.
makeLinksClickable(el.childNodes[i]);
// Now we recursively call ourselves.
}
}
}
// Now we register this function to be called as soon as the document is finished
// loading.
registerOnloadHandler(makeLinksClickable);
But Safari has trouble with string.replace() callbacks:
Apparently Safari has some trouble with the string.replace() method when it comes to passing a function as the second parameter, as we have done in our solution above. See This post for information on how to beat this problem. Then we have a fully-workable, cross-browser solution for converting static URLs and email addresses to links using javascript.