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):
- 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.
- Use the node's
splitText
method cut the text node in three parts.
- Create an
<a>
element.
- Append the email's text node (the second text node from the previous) to this anchor. It's automatically removed from the document.
- Insert this link before the third node.
Demo
Not a chrome extension, but it shows how a chrome extension would behave: http://jsfiddle.net/ckw89/
Chrome extension
(the regexp is based on MongoEngine's EmailField
pattern):
script.js
// Initiate recursion
wrapLink(document.body);
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-x08x0bx0cx0e-x1f!x23-\[\]-x7f]|\[x01-011x0bx0cx0e-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')
wrapLink(node);
} 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
a.appendChild(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.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…