Получение селектора jQuery для элемента

В коде psuedo это то, что я хочу.

var selector = $(this).cssSelectorAsString(); // Made up method...
// selector is now something like: "html>body>ul>li>img[3]"
var element = $(selector);

Причина в том, что мне нужно передать это во внешнюю среду, где строка является единственным способом обмена данными. Затем эта внешняя среда должна отправить результат, вместе с тем, какой элемент обновить. Поэтому мне нужно иметь возможность сериализовать уникальный CSS-селектор для каждого элемента на странице.

Я заметил, что jquery имеет метод selector, но он не работает в этом контексте. Он работает только в том случае, если объект был создан с помощью селектора. Он не работает, если объект был создан с помощью HTML node объекта.

Ответ 1

Теперь я вижу, что существует плагин (с тем же именем, о котором я думал), но вот только какой-то быстрый JavaScript, который я написал. Он не учитывает идентификаторы или классы элементов. только структура (и добавляет :eq(x), где имя node неоднозначно).

jQuery.fn.getPath = function () {
    if (this.length != 1) throw 'Requires one element.';

    var path, node = this;
    while (node.length) {
        var realNode = node[0], name = realNode.localName;
        if (!name) break;
        name = name.toLowerCase();

        var parent = node.parent();

        var siblings = parent.children(name);
        if (siblings.length > 1) { 
            name += ':eq(' + siblings.index(realNode) + ')';
        }

        path = name + (path ? '>' + path : '');
        node = parent;
    }

    return path;
};

Ответ 2

TL; DR - это более сложная проблема, чем кажется, и вы должны использовать .


Эта проблема кажется легкой на первый взгляд, но она сложнее, чем кажется, так же как замена простых URL-ссылок на ссылки нетривиальна. Некоторые соображения:

Дальнейшее доказательство того, что проблема не так проста, как кажется: есть 10 + библиотеки, которые генерируют селектор CSS, а автор одного из них опубликовал это сравнение.

Ответ 3

jQuery-GetPath является хорошей отправной точкой: он даст вам предков, например:

var path = $('#foo').getPath();
// e.g., "html > body > div#bar > ul#abc.def.ghi > li#foo"

Ответ 4

Здесь версия ответа Blixt, которая работает в IE:

jQuery.fn.getPath = function () {
    if (this.length != 1) throw 'Requires one element.';

    var path, node = this;
    while (node.length) {
        var realNode = node[0];
        var name = (

            // IE9 and non-IE
            realNode.localName ||

            // IE <= 8
            realNode.tagName ||
            realNode.nodeName

        );

        // on IE8, nodeName is '#document' at the top level, but we don't need that
        if (!name || name == '#document') break;

        name = name.toLowerCase();
        if (realNode.id) {
            // As soon as an id is found, there no need to specify more.
            return name + '#' + realNode.id + (path ? '>' + path : '');
        } else if (realNode.className) {
            name += '.' + realNode.className.split(/\s+/).join('.');
        }

        var parent = node.parent(), siblings = parent.children(name);
        if (siblings.length > 1) name += ':eq(' + siblings.index(node) + ')';
        path = name + (path ? '>' + path : '');

        node = parent;
    }

    return path;
};

Ответ 5

Я просто хотел поделиться своей версией, потому что это очень понятно. Я тестировал этот script во всех распространенных браузерах и работал как босс.

jQuery.fn.getPath = function () {
    var current = $(this);
    var path = new Array();
    var realpath = "BODY";
    while ($(current).prop("tagName") != "BODY") {
        var index = $(current).parent().find($(current).prop("tagName")).index($(current));
        var name = $(current).prop("tagName");
        var selector = " " + name + ":eq(" + index + ") ";
        path.push(selector);
        current = $(current).parent();
    }
    while (path.length != 0) {
        realpath += path.pop();
    }
    return realpath;
}

Ответ 6

То же решение, что и у @Blixt, но совместимое с несколькими элементами jQuery.

jQuery('.some-selector') может привести к одному или нескольким элементам DOM. Решение @Blixt работает, к сожалению, только с первого. Мое решение объединяет все их с помощью ,.

Если вы хотите просто обработать первый элемент, выполните следующие действия:

jQuery('.some-selector').first().getPath();

// or
jQuery('.some-selector:first').getPath();

Улучшенная версия

jQuery.fn.extend({
    getPath: function() {
        var pathes = [];

        this.each(function(index, element) {
            var path, $node = jQuery(element);

            while ($node.length) {
                var realNode = $node.get(0), name = realNode.localName;
                if (!name) { break; }

                name = name.toLowerCase();
                var parent = $node.parent();
                var sameTagSiblings = parent.children(name);

                if (sameTagSiblings.length > 1)
                {
                    allSiblings = parent.children();
                    var index = allSiblings.index(realNode) +1;
                    if (index > 0) {
                        name += ':nth-child(' + index + ')';
                    }
                }

                path = name + (path ? ' > ' + path : '');
                $node = parent;
            }

            pathes.push(path);
        });

        return pathes.join(',');
    }
});

Ответ 7

Если вы ищете всеобъемлющее решение, отличное от jQuery, тогда вы должны попробовать axe.utils.getSelector.

Ответ 8

Следуя за тем, что написал alex. jQuery-GetPath - отличная отправная точка, но я немного модифицировал его: eq(), позволяя различать несколько элементов без id.

Добавьте это перед линией возврата getPath:

if (typeof id == 'undefined' && cur != 'body') {
    allSiblings = $(this).parent().children(cur);
    var index = allSiblings.index(this);// + 1;
    //if (index > 0) {
        cur += ':eq(' + index + ')';
    //}
}

Это вернет путь как "html > body > ul # hello > li.5: eq (1)"

Ответ 9

Вы также можете взглянуть на findCssSelector, который используется в инструментах разработчика Firefox для сохранения выбранного в данный момент узла при обновлении страницы. Он не использует jQuery или какую-либо библиотеку.

const findCssSelector = function(ele) {
ele = getRootBindingParent(ele);
  let document = ele.ownerDocument;
  if (!document || !document.contains(ele)) {
    throw new Error("findCssSelector received element not inside document");
  }

  let cssEscape = ele.ownerGlobal.CSS.escape;

  // document.querySelectorAll("#id") returns multiple if elements share an ID
  if (ele.id &&
      document.querySelectorAll("#" + cssEscape(ele.id)).length === 1) {
    return "#" + cssEscape(ele.id);
  }

  // Inherently unique by tag name
  let tagName = ele.localName;
  if (tagName === "html") {
    return "html";
  }
  if (tagName === "head") {
    return "head";
  }
  if (tagName === "body") {
    return "body";
  }

  // We might be able to find a unique class name
  let selector, index, matches;
  if (ele.classList.length > 0) {
    for (let i = 0; i < ele.classList.length; i++) {
      // Is this className unique by itself?
      selector = "." + cssEscape(ele.classList.item(i));
      matches = document.querySelectorAll(selector);
      if (matches.length === 1) {
        return selector;
      }
      // Maybe it unique with a tag name?
      selector = cssEscape(tagName) + selector;
      matches = document.querySelectorAll(selector);
      if (matches.length === 1) {
        return selector;
      }
      // Maybe it unique using a tag name and nth-child
      index = positionInNodeList(ele, ele.parentNode.children) + 1;
      selector = selector + ":nth-child(" + index + ")";
      matches = document.querySelectorAll(selector);
      if (matches.length === 1) {
        return selector;
      }
    }
  }

  // Not unique enough yet.  As long as it not a child of the document,
  // continue recursing up until it is unique enough.
  if (ele.parentNode !== document) {
    index = positionInNodeList(ele, ele.parentNode.children) + 1;
    selector = findCssSelector(ele.parentNode) + " > " +
      cssEscape(tagName) + ":nth-child(" + index + ")";
  }

  return selector;

};

Ответ 10

$.fn.getSelector = function(){
    var $ele = $(this);
    return '#' + $ele.parents('[id!=""]').first().attr('id') 
               + ' .' + $ele.attr('class');
};