二叉树祖先节点_查找祖先DOM节点

二叉树祖先节点

对于这个小巧的函数系列的第八篇文章,我将介绍一个名为ancestor()的函数。 顾名思义,该函数根据标签名称和/或类匹配获得对给定节点祖先的引用。

这是ancestor()函数的代码:

function ancestor(node, match)
{
  if(!node)
  {
    return null;
  }
  else if(!node.nodeType || typeof(match) != 'string')
  {
    return node;
  }
  if((match = match.split('.')).length === 1)
  {
    match.push(null);
  }
  else if(!match[0])
  {
    match[0] = null;
  }
  do
  {
    if
    (
      (
        !match[0]
        ||
        match[0].toLowerCase() == node.nodeName.toLowerCase())
      &&
      (
        !match[1]
        ||
        new RegExp('( |^)(' + match[1] + ')( |$)').test(node.className)
      )
    )
    {
      break;
    }
  }
  while(node = node.parentNode);

  return node;
}

第一个参数是对原始节点的引用-可以是任何种类的DOM节点,但通常是一个元素。 第二个参数是一个字符串,用于标识祖先,可以是简单的标记名(如"ul" )或类选择器(如".menu" ,也可以是两者的组合,如"ul.menu" 。 该函数将从原始节点向上迭代,并返回与字符串模式匹配的第一个祖先节点;如果找不到此类祖先,则返回null

函数的作用是什么

此功能最常见的用例是在事件处理代码中-识别事件目标中的包含元素,而不必知道其间还有其他节点。 也许我们甚至都不知道祖先是什么类型的元素。 ancestor()函数通过针对我们拥有的所有信息迭代检查父节点来处理此问题。

例如,假设我们将focus事件绑定到一组菜单链接,并带有需要获取对包含列表项的引用的处理程序代码。 动态菜单通常需要在它们支持的标记类型上非常灵活,不仅要考虑像这样的简单项目:

<li>
  <a>...</a>
</li>

但也包括更复杂的项目,添加了用于额外语义或样式钩子的其他元素:

<li>
  <h3>
    <span>
      <a>...</a>
    </span>
  </h3>
</li>

将添加JavaScript来处理链接focus事件(由于焦点事件不会冒泡 ,因此必须单独添加):

var links = menu.getElementsByTagName('a');

for(var len = links.length, i = 0; i < len; i ++)
{
  links[i].addEventListener('focus', function(e)
  {
    var link = e.target;

  }, false);
}

然后, ancestor()函数可以处理目标转换:

var item = ancestor(link, 'li');

第二个参数的灵活性允许不同的信息情况,例如,我们知道包含菜单将具有"menu" class ,但我们不知道它是<ul>还是<ol>元素:

var menu = ancestor(link, '.menu');

或者,也许我们有一个更深层的结构,其中各个子菜单是无序列表( <ul class="menu"> ),而顶层导航栏是具有相同class名的有序列表( <ol class="menu"> )。 我们可以在匹配class中定义标签名称和class ,以获取所需的特定引用:

var navbar = ancestor(link, 'ol.menu');

在这种情况下的话,任何数量的其他的"menu"的元素将被忽略,如果用它既标签名称和相匹配的祖先只返回class

功能如何运作

基本功能只是通过DOM向上迭代 。 我们从原始节点开始,然后检查每个parentNode直到指定的祖先匹配为止;如果节点用完(即,如果到达#document却找不到所需的节点),则放弃迭代。 但是,我们还有一些测试代码,以确保正确定义了两个参数:

if(!node)
{
  return null;
}
else if(!node.nodeType || typeof(match) != 'string')
{
  return node;
}

如果输入node参数未定义或为null ,则函数返回null ; 或者,如果输入node不是节点,或者输入match不是字符串,则该函数将返回原始节点。 这些只是安全条件,通过减少对发送给它的数据进行预测试的需求,使功能更强大。

接下来,我们处理match参数以创建两个值的数组-第一个是指定的标记名(如果未指定,则为null ),第二个是指定的类名(如果未指定,则为null ):

if((match = match.split('.')).length === 1)
{
  match.push(null);
}
else if(!match[0])
{
  match[0] = null;
}

最后,我们可以进行迭代检查,将每次迭代中的当前参考节点与match数组中定义的标准进行比较。 如果match[0] (标记名称)为null任何元素都将匹配,否则我们仅匹配具有指定标记名称的元素(将两者都转换为小写,因此匹配不区分大小写)。 同样,如果match[1] (类名)为null那么一切都很好,否则该元素必须包含指定的class

do
{
  if
  (
    (
      !match[0]
      ||
      match[0].toLowerCase() == node.nodeName.toLowerCase())
    &&
    (
      !match[1]
      ||
      new RegExp('( |^)(' + match[1] + ')( |$)').test(node.className)
    )
  )
  {
    break;
  }
}
while(node = node.parentNode);

如果两个条件都匹配,我们中断迭代,并返回当前参考节点; 否则,我们继续下一个parentNode 。 如果我们允许代码在两个match值都为null时达到这个目标,那么最终结果将是我们返回原始node ,这正是开始时的安全状况。

关于迭代本身有趣的事情是do...while的使用:

do
{
  ...
}
while(node = node.parentNode);

while评估内部,我们利用了在评估内部定义分配的功能。 每次进行评估时, node引用都将转换为parentNode node并重新分配。 该分配返回分配的node 。 如果父代不存在,则node引用将为null ,因此它不会通过while条件,因此迭代将停止并且函数将返回null 。 但是,如果父节点确实存在,它将通过while条件,因此迭代将继续,因为任何节点引用的评估结果为true ,而null评估结果为false

免费学习PHP!

全面介绍PHP和MySQL,从而实现服务器端编程的飞跃。

原价$ 11.95 您的完全免费

由于我们必须测试的节点数是未知的,因此只要父级存在,就必须使用while语句进行迭代。 但是,通过使用do...while而不是while ,我们可以转换为父节点之前评估原始节点(因为do在第一个while之前评估)。 最终,这意味着如果原始节点已经通过匹配条件,它将立即返回,这使我们不必在迭代之前定义单独的if条件。

结论

ancestor()函数不会赢得任何复杂的奖励! 但是简单功能的抽象是编程的基础,提供了可重复使用的代码,从而节省了重复键入相同的基本逻辑的麻烦。

翻译自: https://www.sitepoint.com/finding-an-ancestor-node/

二叉树祖先节点

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值