Element から CSS Selector を取得する

コード

getCSSSelector.js/**
 * Element から CSS Selector を取得する
 * @author      toshi (https://github.com/k08045kk)
 * @license     MIT License | https://opensource.org/licenses/MIT
 * @version     1
 * @since       1 - 20230211 - 初版
 * @see         https://www.bugbugnow.net/2023/02/get-css-selector.html
 * @see         https://gist.github.com/k08045kk/d239bec86ae9b06c438e7e2bf67575fb
 * @param {Element} element - 要素
 * @return {string} - CSS Selector
 */
var getCSSSelector = function(element) {
  if (!(element && element instanceof Node)
   || !(element.nodeType === Node.ELEMENT_NODE || (element=element.parentElement))) {
    return 'unknown';
  }

  var array = [];
  for (; element; element=element.parentElement) {
    if (element.id) {
      // #id
      array.unshift('#'+element.id)
      break;
    } else {
      // tagName.className:nth-of-type(n)
      var tagName = element.tagName;
      var text = tagName.toLowerCase();

      var list = element.classList;
      var len = list.length;
      for (var i=0; i<len; i++) { text += '.'+list[i]; }

      var n = 0;
      for (var pre=element; (pre=pre.previousElementSibling) && (pre.tagName != tagName || ++n); ) {}
      var nth = n+1;
      if (!n) {
        for (var next=element; (next=next.nextElementSibling) && (next.tagName != tagName || !++n); ) {}
      }
      if (n) {
        text += ':nth-of-type('+nth+')';
      }

      array.unshift(text);
    }
  }
  return array.join(' > ');
};

getCSSSelector.min.js/*! getCSSSelector.js | MIT License | https://gist.github.com/k08045kk */var getCSSSelector=function(a){if(!(a&&a instanceof Node)||a.nodeType!==Node.ELEMENT_NODE&&!(a=a.parentElement))return"unknown";for(var e=[];a;a=a.parentElement)if(a.id){e.unshift("#"+a.id);break}else{for(var f=a.tagName,g=f.toLowerCase(),b=a.classList,c=b.length,d=0;d<c;d++)g+="."+b[d];b=0;for(c=a;(c=c.previousElementSibling)&&(c.tagName!=f||++b););c=b+1;if(!b)for(d=a;(d=d.nextElementSibling)&&(d.tagName!=f||!++b););b&&(g+=":nth-of-type("+c+")");e.unshift(g)}return e.join(" > ")};

更新履歴

備考

document.querySelector()Element を取得可能な CSS Selector を取得します。

ただ、上記コードが完全なコードではなく突き詰めれば多彩なバリエーションを作成できます。例えば次のような関数が考えられます。

  • 行数最小の関数
  • オプションによる高機能関数
  • .className を含まない
  • :nth-of-type(n) を含まない
  • :nth-child(n) で実装する
  • 一部の属性を追記する(<a>href属性等)
  • Element 以外の対策除去(Node or null 対策)
  • XML モード対応(tagName の大文字小文字対策)

上記コードは、「結果の読みやすさ」と「処理の継続性」を考慮して作成しています。「結果の読みやすさ」を無視できるのであれば、.className 部分を削除できます。同様に「処理の継続性」を無視できるのであれば、Nodenullなどへの対応部分を削除できます。tagNameの大文字小文字を考慮すれば、XMLモードに対応することもできます。

使用例(Bookmarklet)

ページ上の要素をクリックすると、 コンソールへ CSS Selector を出力します。

getCSSSelector.bookmarklet.jsjavascript:/*! included (getCSSSelector.js | MIT License | gist.github.com/k08045kk) */(function(){window.addEventListener("click",function(h){var k=console,l=k.log,a;if((a=h.target)&&a instanceof Node&&(a.nodeType===Node.ELEMENT_NODE||(a=a.parentElement))){for(var e=[];a;a=a.parentElement)if(a.id){e.unshift("#"+a.id);break}else{for(var f=a.tagName,g=f.toLowerCase(),b=a.classList,c=b.length,d=0;d<c;d++)g+="."+b[d];b=0;for(c=a;(c=c.previousElementSibling)&&(c.tagName!=f||++b););c=b+1;if(!b)for(d=a;(d=d.nextElementSibling)&&(d.tagName!=f||!++b););b&&(g+=":nth-of-type("+c+")");e.unshift(g)}a=e.join(" > ")}else a="unknown";l.call(k,a,h.target)})})();
無圧縮コード
getCSSSelector.bookmarklet.js/*! included (getCSSSelector.js | MIT License | gist.github.com/k08045kk) */
(function () {
  var getCSSSelector = function(element) {
    if (!(element && element instanceof Node)
     || !(element.nodeType === Node.ELEMENT_NODE || (element=element.parentElement))) {
      return 'unknown';
    }

    var array = [];
    for (; element; element=element.parentElement) {
      if (element.id) {
        // #id
        array.unshift('#'+element.id)
        break;
      } else {
        // tagName.className:nth-of-type(n)
        var tagName = element.tagName;
        var text = tagName.toLowerCase();

        var list = element.classList;
        var len = list.length;
        for (var i=0; i<len; i++) { text += '.'+list[i]; }

        var n = 0;
        for (var pre=element; (pre=pre.previousElementSibling) && (pre.tagName != tagName || ++n); ) {}
        var nth = n+1;
        if (!n) {
          for (var next=element; (next=next.nextElementSibling) && (next.tagName != tagName || !++n); ) {}
        }
        if (n) {
          text += ':nth-of-type('+nth+')';
        }

        array.unshift(text);
      }
    }
    return array.join(' > ');
  };
  window.addEventListener('click', function(event) {
    console.log(getCSSSelector(event.target), event.target);
  });
})();

参考