Skip to content

Instantly share code, notes, and snippets.

@adamnew123456
Created August 16, 2016 17:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save adamnew123456/7858cbc9320cc95af9bc119719fffec0 to your computer and use it in GitHub Desktop.
Save adamnew123456/7858cbc9320cc95af9bc119719fffec0 to your computer and use it in GitHub Desktop.
Various Small DOM-manipulation Utilities
/*
* Returns a list of all the text nodes under the given node.
*
* The order should be depth-first, first-to-last across siblings.
*/
function findTextNodes(node) {
var HTML_TEXT_NODE = 3;
if (node.nodeType == HTML_TEXT_NODE) {
return [node];
}
var text_nodes = [];
var to_search = [];
function recordChildren(node) {
for (var i = 0; i < node.childNodes.length; i++) {
to_search.push(node.childNodes[i]);
}
}
recordChildren(node);
while (to_search.length > 0) {
var node = to_search.shift();
if (node.nodeType == HTML_TEXT_NODE) {
text_nodes.push(node);
} else {
recordChildren(node);
}
}
return text_nodes;
}
/*
* Creates a new text node, and returns it.
*/
function Text(text) {
var node = document.createTextNode(text);
return node;
}
/*
* Takes a text node, and splits it into three parts:
*
* - The text before some range
* - The text of the range
* - The text after some range
*
* Note that the range is of the form [start, end) - i.e. Python style slices/ranges.
*
* Returns the three nodes as a list.
*/
function splitText(text_node, range_start, range_end) {
var before = text_node.textContent.substring(0, range_start);
var range = text_node.textContent.substring(range_start, range_end);
var after = text_node.textContent.substring(range_end);
var before_text = Text(before);
var after_text = Text(after);
text_node.textContent = range;
text_node.parentNode.insertBefore(before_text, text_node);
text_node.parentNode.insertBefore(after_text, text_node.nextSibling);
return [before_text, text_node, after_text];
}
/*
* Wraps a text node with a highlight.
*/
function highlightText(node) {
var highlight_style = document.createElement('span');
highlight_style.style.background = 'yellow';
highlight_style.style.color = 'black';
node.parentNode.replaceChild(highlight_style, node);
highlight_style.appendChild(node);
}
/*
* Highlights the contents of the selection.
*/
function highlightSelection() {
var selection = document.getSelection();
var range = selection.getRangeAt(0);
/*
* Ranges cover a series of nodes in the DOm, like this (caps are in the range):
*
* a
* B c
* D E
*
* The range goes from [B, E] and its common ancestor is a.
*/
var start_node = range.startContainer;
var end_node = range.endContainer;
// These are the actual indexes into the startnig and ending text nodes
var start_idx = range.startOffset;
var end_idx = range.endOffset;
var toplevel_node = range.commonAncestorContainer;
var text_nodes = findTextNodes(toplevel_node);
var in_selection = false;
for (var i = 0; i < text_nodes.length; i++) {
var node = text_nodes[i];
if (node === start_node) {
// If we stay in the same node, then we have to extract a portion of the node
// that goes from the startOffset to the endOffset
if (start_node === end_node) {
var replaced = splitText(node, start_idx, end_idx);
highlightText(replaced[1]);
break;
} else {
in_selection = true;
var replaced = splitText(node, start_idx, node.textContent.length);
highlightText(replaced[1]);
}
} else if (node === end_node) {
var replaced = splitText(node, 0, end_idx);
highlightText(replaced[1]);
break;
} else if (in_selection) {
highlightText(node);
}
}
}
/*
* Redacts the content of a text node.
*/
function redactText(node) {
var block = "■";
var node_length = node.textContent.length;
var blocks = [];
for (var i = 0; i < node_length; i++) {
blocks.push(block);
}
node.textContent = blocks.join('');
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment