DOM树减少使用RAMDA

时间:2016-09-29 19:17:03

标签: javascript functional-programming ramda.js

我想在浏览器中走一个DOM树,收集“叶子”的DOM节点,不包含DOM子节点,只包含文本节点。

我想象有一种方法可以用reduce来做到这一点,但对我来说并不明白如何......递归地减少树状结构。

我已经构建了一堆可用的组件......

let nodeFromJQuery = R.invoker(1,'get')(0);
let nodeFromAny = R.ifElse(R.isArrayLike,nodeFromJQuery,R.identity);
let nodeType = R.pipe(nodeFromAny,R.prop('nodeType'));
let children = R.pipe(nodeFromAny,R.prop('childNodes'));
let textNodeType = R.equals(3);
let domNodeType = R.equals(1);

let domNodes = R.map(isDomNode);
let textNodes = R.map(isTextNode);

let isTextNode = R.pipe(nodeType, textNodeType);
let isDomNode = R.pipe(nodeType,domNodeType);
let domChildren = R.pipe(children,R.filter(isDomNode));

isLeaf = R.pipe(domChildren, R.isEmpty);

getNodes = R.filter(R.not(isLeaf));
getLeaves = R.filter(isLeaf)

但我没有看到简单的减少......有什么想法吗?

谢谢,

1 个答案:

答案 0 :(得分:2)

您可以采用的一种方法是使用Node方法为reduce实例创建一个包装器类型,Ramda可以调度该方法,允许您汇总Node个实例的整个树

const node = (n) => ({
  reduce(f, z) {
    switch(n.nodeType) {
      case Node.TEXT_NODE:
        return f(z, n)
      case Node.ELEMENT_NODE:
        return R.reduce(
          (_z, _n) => node(_n).reduce(f, _z),
          f(z, n),
          n.childNodes
        )
      default:
        return z // ignore other node types
    }
  }
})

如果您对收集Text个节点的列表感兴趣,现在可以通过reduce选择性地将它们添加到列表中。

const isInterestingTextNode = R.both(
  R.propEq('nodeType', Node.TEXT_NODE),
  R.propSatisfies(R.complement(R.test(/^\s*$/)), 'textContent')
)

const textNodesOf = R.pipe(node, R.reduce((textNodes, node) => {
  if (isInterestingTextNode(node)) textNodes.push(node.textContent)
  return textNodes
}, []))

您可以在下面的代码段中看到此示例。



const node = (n) => ({
  reduce(f, z) {
    switch(n.nodeType) {
      case Node.TEXT_NODE:
        return f(z, n)
      case Node.ELEMENT_NODE:
        return R.reduce(
          (_z, _n) => node(_n).reduce(f, _z),
          f(z, n),
          n.childNodes
        )
      default:
        return z // ignore other node types
    }
  }
})

const isInterestingTextNode = R.both(
  R.propEq('nodeType', Node.TEXT_NODE),
  R.propSatisfies(R.complement(R.test(/^\s*$/)), 'textContent')
)

const textNodesOf = R.pipe(node, R.reduce((textNodes, node) => {
  if (isInterestingTextNode(node)) textNodes.push(node.textContent)
  return textNodes
}, []))

console.log(textNodesOf(document.getElementById('root')))

<!DOCTYPE html>
<html>
<head>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.22.1/ramda.min.js"></script>
</head>
<body>
  <div id="root">
    <div>
      <span>foo</span>
    </div>
    <div>
      <span>bar</span>
    </div>
  </div>
</body>
</html>
&#13;
&#13;
&#13;