How to replace email addresses with mailto links in a live web page?

Imagine this: you come across a webpage that says "Just send a message to [email protected]" but to actually send the email, you need to highlight the address and then cut and paste it into the recipient field of a new compose window of your email client of choice.

Obviously life would be easier if it were simply a mailto: link, so you could click on it and have a new message created automatically. How do I build an extension that turns email addresses into clickable mailto: links?

I was originally going to ask if there was an extension to enable similar functionality for unlinked Twitter @username mentions but I thought this email address problem would be a simpler situation.



Use innerHTML to replace the email - No, this breaks page's, especially because event listeners are removed and attributes are also replaced.

Recursively loop through all nodes:

  • Loop in the reverse order, to prevent conflicts when you modify the DOM.
  • For each item, check it's nodeType value.
    • If .nodeType === 1 (element), call the function again (recursion).
    • If .nodeType === 3 (text node):
      1. Use a regular expression and the exec method to find one email address. Use the result's .index property to know the starting position of the email address, and result[0].length to know the length of the address.
      2. Use the node's splitText method cut the text node in three parts.
      3. Create an <a> element.
      4. Append the email's text node (the second text node from the previous) to this anchor. It's automatically removed from the document.
      5. Insert this link before the third node.


Not a chrome extension, but it shows how a chrome extension would behave:

Chrome extension

(the regexp is based on MongoEngine's EmailField pattern):


 // Initiate recursion

 function wrapLink(elem) { // elem must be an element node
     var nodes = elem.childNodes
       , i = nodes.length
       , regexp = /([-!\x23$%&'*+\/=?^_`{}|~0-9A-Z]+(\.[-!\x23$%&'*+\/=?^_`{}|~0-9A-Z]+)*|^"([\x01-\x08\x0b\x0c\x0e-\x1f!\x23-\\[\\]-\x7f]|\\[\x01-011\x0b\x0c\x0e-\x7f])*")@(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+[A-Z]{2,6}/i
       , node, emailNode, a, result;
     while (node = nodes[--i]) {
         if (node.nodeType === 1) {
             // Skip anchor tags, nested anchors make no sense
             if (node.nodeName.toUpperCase() !== 'A')
         } else if (node.nodeType === 3) {
             // 1: Please note that the regexp has NO global flag,
             //    and that `node.textContent` shrinks when an address is found
             while (result = regexp.exec(node.textContent)) {
                 // 2: Contact <SPLIT> [email protected] for details
                 node = node.splitText(result.index);

                 // 2: Contact <SPLIT>[email protected]<SPLIT> for details
                 node = node.splitText(result[0].length);

                 // [email protected]
                 emailNode = node.previousSibling

                 // 3. Create link
                 a = document.createElement('a');
                 a.href = 'mailto:' + result[0];

                 // 4: Append emailNode

                 // 5: Insert before
                 elem.insertBefore(a, node);

This script will work immediately when used as a Content script, because its only interaction with the page is the DOM. For completeness, here's the contents of the manifest.json file:

    "name": "Turns email addresses in `mailto:`s",
    "version": "1",
    "version_version": 2,
    "content_scripts": [{
        "matches": ["*://*/*"],
        "js": ["script.js"]

Performance notice

The current script replaces all nodes in the live document. Consider moving the root node (eg <body>) to a document fragment before manipulating.


Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.