Clickable URLs with javascript

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.

comments powered by Disqus