Подтвердить что ты не робот

Получите уникальный селектор элемента в JQuery

Я хочу создать что-то вроде рекордера, который отслеживает все действия пользователя. Для этого мне нужно идентифицировать элементы, с которыми пользователь взаимодействует, поэтому я могу ссылаться на эти элементы в более позднем сеансе.

Говоря в псевдокоде, я хочу сделать что-то вроде следующего

Пример HTML (может быть любой сложности):

<html>
<body>
  <div class="example">
    <p>foo</p>
    <span><a href="bar">bar</a></span>
  </div>
</body>
</html>

Пользователь нажимает на что-то, как на ссылку. Теперь мне нужно идентифицировать элемент с щелчком и сохранить его местоположение в дереве DOM для последующего использования:

(any element).onclick(function() {
  uniqueSelector = $(this).getUniqueSelector();
})

Теперь уникальныйSelector должен быть чем-то вроде (я не против, если это стиль выбора xpath или css):

html > body > div.example > span > a

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

Как это возможно?

Update

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

4b9b3361

Ответ 1

Я сам ответю на это, потому что нашел решение, которое мне пришлось изменить. Следующий скрипт работает и основан на скрипте Blixt:

jQuery.fn.extend({
    getPath: function () {
        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 sameTagSiblings = parent.children(name);
            if (sameTagSiblings.length > 1) { 
                var allSiblings = parent.children();
                var index = allSiblings.index(realNode) + 1;
                if (index > 1) {
                    name += ':nth-child(' + index + ')';
                }
            }

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

        return path;
    }
});

Ответ 2

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

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

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

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)
                {
                    var 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(',');
    }
});

Ответ 3

Я думаю, что лучшим решением было бы создать случайный идентификатор, а затем получить доступ к элементу, основанному на этом id:

Назначение уникального идентификатора:

// or some other id-generating algorithm
$(this).attr('id', new Date().getTime()); 

Выбор на основе уникального идентификатора:

// getting unique id
var uniqueId = $(this).getUniqueId();

// or you could just get the id:
var uniqueId = $(this).attr('id');

// selecting by id:
var element = $('#' + uniqueId);

// if you decide to use another attribute other than id:
var element = $('[data-unique-id="' + uniqueId + '"]');

Ответ 4

(any element).onclick(function() {
  uniqueSelector = $(this).getUniqueSelector();
})

this IS уникальный селектор и путь к этому нажатому элементу. Почему бы не использовать это? Вы можете использовать метод jquery $.data() для установки селектора jquery. Или просто нажмите на элементы, которые вам понадобятся в будущем:

var elements = [];
(any element).onclick(function() {
  elements.push(this);
})

Если вам действительно нужен xpath, вы можете рассчитать его, используя следующий код:

  function getXPath(node, path) {
    path = path || [];
    if(node.parentNode) {
      path = getXPath(node.parentNode, path);
    }

    if(node.previousSibling) {
      var count = 1;
      var sibling = node.previousSibling
      do {
        if(sibling.nodeType == 1 && sibling.nodeName == node.nodeName) {count++;}
        sibling = sibling.previousSibling;
      } while(sibling);
      if(count == 1) {count = null;}
    } else if(node.nextSibling) {
      var sibling = node.nextSibling;
      do {
        if(sibling.nodeType == 1 && sibling.nodeName == node.nodeName) {
          var count = 1;
          sibling = null;
        } else {
          var count = null;
          sibling = sibling.previousSibling;
        }
      } while(sibling);
    }

    if(node.nodeType == 1) {
      path.push(node.nodeName.toLowerCase() + (node.id ? "[@id='"+node.id+"']" : count > 0 ? "["+count+"]" : ''));
    }
    return path;
  };

Ссылка: http://snippets.dzone.com/posts/show/4349

Ответ 5

Хотя вопрос был о jQuery, в ES6, довольно легко получить что-то похожее на @Alp для Vanilla JavaScript (я также добавил пару строк, отслеживая nameCount, чтобы свести к минимуму использование nth-child):

function getSelectorForElement (elem) {
    let path;
    while (elem) {
        let subSelector = elem.localName;
        if (!subSelector) {
            break;
        }
        subSelector = subSelector.toLowerCase();

        const parent = elem.parentElement;

        if (parent) {
            const sameTagSiblings = parent.children;
            if (sameTagSiblings.length > 1) {
                let nameCount = 0;
                const index = [...sameTagSiblings].findIndex((child) => {
                    if (elem.localName === child.localName) {
                        nameCount++;
                    }
                    return child === elem;
                }) + 1;
                if (index > 1 && nameCount > 1) {
                    subSelector += ':nth-child(' + index + ')';
                }
            }
        }

        path = subSelector + (path ? '>' + path : '');
        elem = parent;
    }
    return path;
}

Ответ 6

Чистое решение для JavaScript

Примечание. Для этого используются файлы Array.from и Array.prototype.filter, которые должны быть заполнены в IE11.

function getUniqueSelector(node) {
  let selector = "";
  while (node.parentElement) {
    const siblings = Array.from(node.parentElement.children).filter(
      e => e.tagName === node.tagName
    );
    selector =
      (siblings.indexOf(node)
        ? '${node.tagName}:nth-of-type(${siblings.indexOf(node) + 1})'
        : '${node.tagName}') + '${selector ? " > " : ""}${selector}';
    node = node.parentElement;
  }
  return 'html > ${selector.toLowerCase()}';
}

использование

getUniqueSelector(document.getElementsByClassName('SectionFour')[0]);

getUniqueSelector(document.getElementById('content'));

Ответ 7

Этот ответ не удовлетворяет первоначальному описанию вопроса, однако он отвечает на вопрос заголовка. Я пришел к этому вопросу, ища способ получить уникальный селектор для элемента, но у меня не было необходимости, чтобы селектор был действительным между загрузками страниц. Таким образом, мой ответ не будет работать между загрузками страниц.

Мне кажется, что модификация DOM не является idel, но это хороший способ создать селектор, который является уникальным без тон кода. Я получил эту идею после прочтения ответа @Eli:

Назначьте настраиваемый атрибут с уникальным значением.

$(element).attr('secondary_id', new Date().getTime())
var secondary_id = $(element).attr('secondary_id');

Затем используйте этот уникальный идентификатор для создания CSS-селектора.

var selector = '[secondary_id='+secondary_id+']';

Затем у вас есть селектор, который выберет ваш элемент.

var found_again = $(selector);

И вы много хотите проверить, чтобы убедиться, что в элементе уже нет атрибута secondary_id.

if ($(element).attr('secondary_id')) {
  $(element).attr('secondary_id', (new Date()).getTime());
}
var secondary_id = $(element).attr('secondary_id');

Объединяя все это

$.fn.getSelector = function(){
  var e = $(this);

  // the `id` attribute *should* be unique.
  if (e.attr('id')) { return '#'+e.attr('id') }

  if (e.attr('secondary_id')) {
    return '[secondary_id='+e.attr('secondary_id')+']'
  }

  $(element).attr('secondary_id', (new Date()).getTime());

  return '[secondary_id='+e.attr('secondary_id')+']'
};

var selector = $('*').first().getSelector();

Ответ 8

Если у вас есть атрибут идентификации (например, id = "something" ), вы должны получить его значение, например,

var selector = "[id='" + $(yourObject).attr("id") + "']";
console.log(selector); //=> [id='something']
console.log($(selector).length); //=> 1

Если у вас нет атрибута идентификации, и вы хотите его выбрать, вы можете создать атрибут идентификации. Что-то вроде выше,

var uuid = guid();
$(yourObject).attr("id", uuid); // Set the uuid as id of your object.

Вы можете использовать свой собственный метод guid или использовать исходный код, найденный в этом, поэтому ответьте

function guid() {
    function s4() {
        return Math.floor((1 + Math.random()) * 0x10000)
                .toString(16)
                .substring(1);
    }
    return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
            s4() + '-' + s4() + s4() + s4();
}

Ответ 10

Я нашел для себя какое-то модифицированное решение. Я добавил к селектору пути #id,.className и сократил длину пути до #id:

$.fn.extend({
            getSelectorPath: function () {
                var path,
                    node = this,
                    realNode,
                    name,
                    parent,
                    index,
                    sameTagSiblings,
                    allSiblings,
                    className,
                    classSelector,
                    nestingLevel = true;

                while (node.length && nestingLevel) {
                    realNode = node[0];
                    name = realNode.localName;

                    if (!name) break;

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

                    if (realNode.id) {
                        name += "#" + node[0].id;

                        nestingLevel = false;

                    } else if (realNode.className.length) {
                        className =  realNode.className.split(' ');
                        classSelector = '';

                        className.forEach(function (item) {
                            classSelector += '.' + item;
                        });

                        name += classSelector;

                    } else if (sameTagSiblings.length > 1) {
                        allSiblings = parent.children();
                        index = allSiblings.index(realNode) + 1;

                        if (index > 1) {
                            name += ':nth-child(' + index + ')';
                        }
                    }

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

                return path;
            }
        });

Ответ 11

Вы можете сделать что-то вроде этого:

$(".track").click(function() {
  recordEvent($(this).attr("id"));
});

Он прикрепляет обработчик события onclick к каждому объекту с классом track. Каждый раз, когда объект кликается, его идентификатор подается в функцию recordEvent(). Вы можете заставить эту функцию записывать время и идентификатор каждого объекта или того, что вы хотите.

Ответ 12

$(document).ready(function() {
    $("*").click(function(e) {
        var path = [];
        $.each($(this).parents(), function(index, value) {
            var id = $(value).attr("id");
            var class = $(value).attr("class");
            var element = $(value).get(0).tagName
                path.push(element + (id.length > 0 ? " #" + id : (class.length > 0 ? " .": "") + class));
        });
        console.log(path.reverse().join(">"));
        return false;
    });
});

Рабочий пример: http://jsfiddle.net/peeter/YRmr5/

Вероятно, вы столкнетесь с проблемами при использовании селектора * (очень медленно) и прекращении события от пузырьков вверх, но не можете реально помочь там без кода HTML.

Ответ 13

Вы можете сделать что-то вроде этого (untested)

function GetPathToElement(jElem)
{
   var tmpParent = jElem;
   var result = '';
   while(tmpParent != null)
   {
       var tagName = tmpParent.get().tagName;
       var className = tmpParent.get().className;
       var id = tmpParent.get().id;
       if( id != '') result = '#' + id + result;
       if( className !='') result = '.' + className + result;
       result = '>' + tagName + result;
       tmpParent = tmpParent.parent();
    }
    return result;
}

эта функция сохранит "путь" к элементу, теперь, чтобы снова найти элемент в будущем, будет почти невозможно, как html, потому что в этой функции я не сохраняю индекс sibblling каждого элемента, я сохраняйте только идентификаторы (id) и классы.

Поэтому, если каждый элемент вашего html-документа не имеет идентификатора, этот подход не будет работать.