获取所点击<a></a>的DOM路径

时间:2011-04-20 10:00:57

标签: javascript jquery html dom css-selectors

HTML

<body>
<div class="lol">
<a class="rightArrow" href="javascriptVoid:(0);" title"Next image">
</div>
</body>

伪代码

$(".rightArrow").click(function() {
rightArrowParents = this.dom(); //.dom(); is the pseudo function ... it should show the whole
alert(rightArrowParents);
});

警告信息将是:

body div.lol a.rightArrow

如何使用javascript / jquery获取此内容?

9 个答案:

答案 0 :(得分:37)

这是一个返回jQuery路径的本机JS版本。如果有元素,我也会为元素添加ID。如果您在数组中看到id,这将使您有机会执行最短路径。

var path = getDomPath(element);
console.log(path.join(' > '));

输出

body > section:eq(0) > div:eq(3) > section#content > section#firehose > div#firehoselist > article#firehose-46813651 > header > h2 > span#title-46813651

这是功能。

function getDomPath(el) {
  var stack = [];
  while ( el.parentNode != null ) {
    console.log(el.nodeName);
    var sibCount = 0;
    var sibIndex = 0;
    for ( var i = 0; i < el.parentNode.childNodes.length; i++ ) {
      var sib = el.parentNode.childNodes[i];
      if ( sib.nodeName == el.nodeName ) {
        if ( sib === el ) {
          sibIndex = sibCount;
        }
        sibCount++;
      }
    }
    if ( el.hasAttribute('id') && el.id != '' ) {
      stack.unshift(el.nodeName.toLowerCase() + '#' + el.id);
    } else if ( sibCount > 1 ) {
      stack.unshift(el.nodeName.toLowerCase() + ':eq(' + sibIndex + ')');
    } else {
      stack.unshift(el.nodeName.toLowerCase());
    }
    el = el.parentNode;
  }

  return stack.slice(1); // removes the html element
}

答案 1 :(得分:30)

使用jQuery,就像这样(后面是一个不使用jQuery的解决方案,除了事件;更少的函数调用,如果这很重要):

$(".rightArrow").click(function() {
  var rightArrowParents = [];
  $(this).parents().addBack().not('html').each(function() {
    var entry = this.tagName.toLowerCase();
    if (this.className) {
      entry += "." + this.className.replace(/ /g, '.');
    }
    rightArrowParents.push(entry);
  });
  alert(rightArrowParents.join(" "));
  return false;
});

直播示例:

$(".rightArrow").click(function() {
  var rightArrowParents = [];
  $(this).parents().addBack().not('html').each(function() {
    var entry = this.tagName.toLowerCase();
    if (this.className) {
      entry += "." + this.className.replace(/ /g, '.');
    }
    rightArrowParents.push(entry);
  });
  alert(rightArrowParents.join(" "));
  return false;
});
<div class="lol multi">
  <a href="#" class="rightArrow" title="Next image">Click here</a>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

(在实际示例中,我已将class上的div属性更新为lol multi,以演示处理多个类。)

使用parents获取所点击元素的祖先,通过nothtml开始删除body元素,然后从a开始,然后循环为每个父项创建条目并将它们推送到数组上。然后我们使用addBackparents添加回集合中,这也会将集合的顺序更改为您想要的内容(addBAck是特殊的,它会为您提供相反的父级您想要的顺序,但然后Array#join将其按DOM顺序放回)。然后它使用className创建以空格分隔的字符串。

创建条目时,如果.上有任何内容,我们会使用<p class='foo bar'>替换空格,以支持包含多个类的元素(className具有"foo bar" = {{ 1}},以便条目最终成为p.foo.bar)。

为了完整起见,这是jQuery可能过度杀伤的地方之一,你可以通过走DOM来做到这一点:

$(".rightArrow").click(function() {
  var rightArrowParents = [],
    elm,
    entry;

  for (elm = this; elm; elm = elm.parentNode) {
    entry = elm.tagName.toLowerCase();
    if (entry === "html") {
      break;
    }
    if (elm.className) {
      entry += "." + elm.className.replace(/ /g, '.');
    }
    rightArrowParents.push(entry);
  }
  rightArrowParents.reverse();
  alert(rightArrowParents.join(" "));
  return false;
});

直播示例:

$(".rightArrow").click(function() {
  var rightArrowParents = [],
    elm,
    entry;

  for (elm = this; elm; elm = elm.parentNode) {
    entry = elm.tagName.toLowerCase();
    if (entry === "html") {
      break;
    }
    if (elm.className) {
      entry += "." + elm.className.replace(/ /g, '.');
    }
    rightArrowParents.push(entry);
  }
  rightArrowParents.reverse();
  alert(rightArrowParents.join(" "));
  return false;
});
<div class="lol multi">
  <a href="#" class="rightArrow" title="Next image">Click here</a>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

我们只是反复使用元素的标准parentNode property向上走,直到我们用完父母或者看到html元素。然后我们反转我们的数组(因为它向后移动到你想要的输出),并加入它,我们很高兴。

答案 2 :(得分:10)

我需要一个本机JS版本,它返回CSS标准路径(不是jQuery),并处理ShadowDOM。此代码是迈克尔康纳答案的一个小更新,以防万一其他人需要它:

function getDomPath(el) {
  if (!el) {
    return;
  }
  var stack = [];
  var isShadow = false;
  while (el.parentNode != null) {
    // console.log(el.nodeName);
    var sibCount = 0;
    var sibIndex = 0;
    // get sibling indexes
    for ( var i = 0; i < el.parentNode.childNodes.length; i++ ) {
      var sib = el.parentNode.childNodes[i];
      if ( sib.nodeName == el.nodeName ) {
        if ( sib === el ) {
          sibIndex = sibCount;
        }
        sibCount++;
      }
    }
    // if ( el.hasAttribute('id') && el.id != '' ) { no id shortcuts, ids are not unique in shadowDom
    //   stack.unshift(el.nodeName.toLowerCase() + '#' + el.id);
    // } else
    var nodeName = el.nodeName.toLowerCase();
    if (isShadow) {
      nodeName += "::shadow";
      isShadow = false;
    }
    if ( sibCount > 1 ) {
      stack.unshift(nodeName + ':nth-of-type(' + (sibIndex + 1) + ')');
    } else {
      stack.unshift(nodeName);
    }
    el = el.parentNode;
    if (el.nodeType === 11) { // for shadow dom, we
      isShadow = true;
      el = el.host;
    }
  }
  stack.splice(0,1); // removes the html element
  return stack.join(' > ');
}

答案 3 :(得分:3)

这是一个精确匹配元素的解决方案。

重要的是要理解,chrome工具显示的选择器它不是真正的)不能唯一地标识DOM中的元素。 (例如,它不会区分连续的span元素列表。没有定位/索引信息

来自similar (about xpath) answer

的改编
$.fn.fullSelector = function () {
    var path = this.parents().addBack();
    var quickCss = path.get().map(function (item) {
        var self = $(item),
            id = item.id ? '#' + item.id : '',
            clss = item.classList.length ? item.classList.toString().split(' ').map(function (c) {
                return '.' + c;
            }).join('') : '',
            name = item.nodeName.toLowerCase(),
            index = self.siblings(name).length ? ':nth-child(' + (self.index() + 1) + ')' : '';

        if (name === 'html' || name === 'body') {
            return name;
        }
        return name + index + id + clss;

    }).join(' > ');

    return quickCss;
};

你可以像这样使用它

console.log( $('some-selector').fullSelector() );

http://jsfiddle.net/gaby/zhnr198y/

演示

答案 4 :(得分:2)

我从T.J.移动了片段。克服一个小小的jQuery插件。我使用了他的jQuery版本,即使他是对的,这是完全不必要的开销,但我只是用于调试目的,所以我不在乎。

用法:

<强> HTML

<html>
<body>
    <!-- Two spans, the first will be chosen -->
    <div>
        <span>Nested span</span>
    </div>
    <span>Simple span</span>

    <!-- Pre element -->
    <pre>Pre</pre>
</body>
</html>

<强>的Javascript

// result (array): ["body", "div.sampleClass"]
$('span').getDomPath(false)

// result (string): body > div.sampleClass
$('span').getDomPath()

// result (array): ["body", "div#test"]
$('pre').getDomPath(false)

// result (string): body > div#test
$('pre').getDomPath()

<强>存储库

https://bitbucket.org/tehrengruber/jquery.dom.path

答案 5 :(得分:0)

$(".rightArrow")
  .parents()
  .map(function () { 
      var value = this.tagName.toLowerCase();
      if (this.className) {
          value += '.' + this.className.replace(' ', '.', 'g');
      }
      return value;
  })
  .get().reverse().join(", ");

答案 6 :(得分:0)

    var obj = $('#show-editor-button'),
       path = '';
    while (typeof obj.prop('tagName') != "undefined"){
        if (obj.attr('class')){
            path = '.'+obj.attr('class').replace(/\s/g , ".") + path;
        }
        if (obj.attr('id')){
            path = '#'+obj.attr('id') + path;
        }
        path = ' ' +obj.prop('tagName').toLowerCase() + path;
        obj = obj.parent();
    }
    console.log(path);

答案 7 :(得分:0)

你好这个函数解决了与当前元素相关的bug没有在路径中显示

立即查看

$j(".wrapper").click(function(event) {
      selectedElement=$j(event.target);

      var rightArrowParents = [];
      $j(event.target).parents().not('html,body').each(function() {
          var entry = this.tagName.toLowerCase();
          if (this.className) {
              entry += "." + this.className.replace(/ /g, '.');
          }else if(this.id){
              entry += "#" + this.id;
          }
          entry=replaceAll(entry,'..','.');
          rightArrowParents.push(entry);
      });
      rightArrowParents.reverse();
      //if(event.target.nodeName.toLowerCase()=="a" || event.target.nodeName.toLowerCase()=="h1"){
        var entry = event.target.nodeName.toLowerCase();
        if (event.target.className) {
              entry += "." + event.target.className.replace(/ /g, '.');
        }else if(event.target.id){
              entry += "#" + event.target.id;
        }
        rightArrowParents.push(entry);
     // }

其中$j = jQuery变量

还解决了班级名称

中的问题

这里是替换功能:

function escapeRegExp(str) {
    return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
}
  function replaceAll(str, find, replace) {
  return str.replace(new RegExp(escapeRegExp(find), 'g'), replace);
}

由于

答案 8 :(得分:0)

我一直在使用 Michael Connor's answer 并对其进行了一些改进。

  • 使用 ES6 语法
  • 使用 nth-of-type 而不是 nth-child,因为 nth-of-type 查找相同类型的子项,而不是任何子项
  • 以更简洁的方式移除 html 节点
  • 忽略带有 nodeName 的元素的 id
  • 仅显示最近的 id 之前的路径(如果有)。这应该会使代码更具弹性,但是如果您不想要这种行为,我会在要删除的行上留下评论
  • 使用 CSS.escape 处理 ID 和节点名称中的特殊字符

~

export default function getDomPath(el) {
  const stack = []

  while (el.parentNode !== null) {
    let sibCount = 0
    let sibIndex = 0
    for (let i = 0; i < el.parentNode.childNodes.length; i += 1) {
      const sib = el.parentNode.childNodes[i]
      if (sib.nodeName === el.nodeName) {
        if (sib === el) {
          sibIndex = sibCount
          break
        }
        sibCount += 1
      }
    }

    const nodeName = CSS.escape(el.nodeName.toLowerCase())

    // Ignore `html` as a parent node
    if (nodeName === 'html') break

    if (el.hasAttribute('id') && el.id !== '') {
      stack.unshift(`#${CSS.escape(el.id)}`)
      // Remove this `break` if you want the entire path
      break
    } else if (sibIndex > 0) {
      // :nth-of-type is 1-indexed
      stack.unshift(`${nodeName}:nth-of-type(${sibIndex + 1})`)
    } else {
      stack.unshift(nodeName)
    }

    el = el.parentNode
  }

  return stack
}