Skip to content

Instantly share code, notes, and snippets.

@tilgovi
Last active October 8, 2015 00:09
Show Gist options
  • Save tilgovi/04c13e202c5becdd1d21 to your computer and use it in GitHub Desktop.
Save tilgovi/04c13e202c5becdd1d21 to your computer and use it in GitHub Desktop.
DOM tree traversal helpers
# Return true if the given predicate is true for every Node of the tree.
# The predicate function is invoked once for each Node in the tree.
# An optional third argument specifies a traversal order and should be a
# function that takes a Node and returns its successor. The default order is
# DOM position order (pre-order).
everyNode = (node, fn, next = preFirst) ->
return !someNode(node, ((n) -> !fn(n)), next)
# Return the first Node in the tree that matches the predicate.
# An optional third argument specifies a traversal and should be a function
# that returns the successor of its argument. The default is document order.
findNode = (node, fn, next = preFirst) ->
node = next(node) while node? and not result = fn(node)
return (if result then node else undefined)
# Return true if the given predicate is true for any Node of the tree.
# An optional third argument specifies a traversal and should be a function
# that returns the successor of its argument. The default is document order.
someNode = (node, fn, next = preFirst) ->
return !!findNode(node, fn, next)
# Return an Array of each Node in the tree for which the predicate is true.
# An optional third argument specifies a traversal and should be a function
# that returns the successor of its argument. The default is document order.
filterNode = (node, fn, next = preFirst) ->
collect = (acc, n) -> if fn(n) then acc.concat([n]) else acc
return reduceNode(node, collect, [], next)
# Return an Array of the result of applying a function to each Node in a tree.
# An optional third argument specifies a traversal and should be a function
# that returns the successor of its argument. The default is document order.
mapNode = (node, fn, next = preFirst) ->
return reduceNode(node, ((acc, n) -> acc.push(fn(n))), [], next)
reduceNode = (node, fn, initialValue = undefined, next = preFirst) ->
acc = initialValue
last = lastLeaf(node)
if arguments.length is 2
if node is last then return node
acc = node
node = preFirst(node)
else
acc = fn(acc, node)
while node = preFirst(node)
acc = fn(acc, node)
if node is last then break
return acc
# Split a TextNode at an offset, returning the successor.
# https://github.com/Raynos/DOM-shim/issues/11
splitText = (node, offset) ->
tail = node.cloneNode(false)
tail.deleteData(0, offset)
node.deleteData(offset, node.length - offset)
return insertAfter(tail, node)
# Predicate for checking if a node is a TextNode.
isTextNode = (node) ->
return node.nodeType is TEXT_NODE
# Return the next Node in a first-to-last-child pre-order traversal.
preFirst = (node) ->
if node.firstChild?
return node.firstChild
while not node.nextSibling?
node = node.parentNode
if node is null then return null
return node.nextSibling
# Return the next Node in a last-to-first-child pre-order traversal.
preLast = (node) ->
if node.lastChild?
return node.lastChild
while not node.previousSibling?
node = node.parentNode
if node is null then return null
return node.previousSibling
# Find the first leaf node.
firstLeaf = (node) ->
node = node.firstChild while node.hasChildNodes()
return node
# Find the last leaf node.
lastLeaf = (node) ->
node = node.lastChild while node.hasChildNodes()
return node
# Find a common ancestor Node.
commonAncestor = (node, other) ->
while node? and not contains(node, other)
node = node.parentNode
return node
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment