Уникальный идентификатор элемента, даже если у элемента его нет - PullRequest
29 голосов
/ 22 октября 2008

Я пишу скрипт GreaseMonkey, в котором я перебираю кучу элементов. Для каждого элемента мне нужен идентификатор строки, который я могу использовать для ссылки на этот элемент позже. Сам элемент не имеет атрибута id, и я не могу изменить исходный документ, чтобы получить его (хотя я могу вносить изменения в DOM в моем скрипте). Я не могу сохранить ссылки в моем скрипте, потому что, когда они мне понадобятся, сам скрипт GreaseMonkey выйдет из области видимости. Есть ли способ, например, получить «внутренний» идентификатор, который использует браузер? Подходит только для Firefox; кросс-браузерное решение, которое может быть применено в других сценариях, было бы замечательно.

Edit:

  • Если сценарий GreaseMonkey находится вне области действия, как вы будете ссылаться на элементы позже? Они сценария GreaseMonkey добавляют события к объектам DOM. Я не могу сохранить ссылки в массиве или каком-либо другом подобном механизме, потому что, когда событие сработает, массив исчезнет, ​​потому что скрипт GreaseMonkey выйдет из области видимости. Таким образом, событию нужен какой-то способ узнать ссылку на элемент, которая была в сценарии, когда событие было присоединено. И рассматриваемый элемент не тот, к которому он прикреплен.

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

  • Подождите, вы можете или не можете изменить документ? Я не могу изменить исходный документ, но могу внести изменения в DOM в сценарии. Я уточню в вопросе.

  • Разве вы не можете использовать замыкания? Закрытия оказались эффективными, хотя я изначально думал, что они не будут. Смотрите мой следующий пост.

Звучит как ответ на вопрос: "Могу ли я использовать какой-нибудь внутренний идентификатор браузера?" это "Нет."

Ответы [ 13 ]

7 голосов
/ 22 октября 2008

Ответ - нет, нет внутреннего идентификатора, к которому вы можете получить доступ. Opera и IE (возможно, Safari?) Поддерживают .sourceIndex (что меняется, если DOM делает), но Firefox не имеет ничего подобного.

Вы можете смоделировать source-index, генерируя Xpath для данного узла или находя индекс узла из document.getElementsByTagName('*'), который всегда будет возвращать элементы в исходном порядке.

Все это, конечно, требует полностью статического файла. Изменения в DOM сломают поиск.

Чего я не понимаю, так это как вы можете потерять ссылки на узлы, но не на (теоретические) внутренние идентификаторы? Либо замыкания и назначения работают, либо нет. Или я что-то упустил?

6 голосов
/ 22 октября 2008

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

Пример для тех, кто не знает замыканий:

var saved_element = findThatDOMNode();

document.body.onclick = function() 
{
   alert(saved_element); // it's still there!
}

Если вам нужно было сохранить его в cookie, то я рекомендую вычислить для него XPath (например, пройтись по DOM, считая предыдущих братьев и сестер, пока не найдете элемент с идентификатором, и в итоге вы получите что-то как [@id=foo]/div[4]/p[2]/a).

XPointer - решение W3C для этой проблемы.

6 голосов
/ 22 октября 2008

Немного смущен формулировкой вашего вопроса - вы говорите, что вам «нужен строковый идентификатор, который [вы] можете использовать для ссылки на этот элемент позже», но что вы «не можете хранить ссылки в [вашем] скрипте потому что когда они вам понадобятся, сам скрипт GreaseMonkey выйдет из области видимости. "

Если скрипт выйдет из области видимости, то как вы будете ссылаться на них позже?!

Я собираюсь игнорировать тот факт, что меня смущает то, к чему вы клоните, и говорить, что я пишу сценарии Greasemonkey довольно часто, и может изменять элементы DOM, к которым я обращаюсь, чтобы дать им свойство ID , Этот код вы можете использовать для получения псевдо-уникального значения для временного использования:

var PseudoGuid = new (function() {
    this.empty = "00000000-0000-0000-0000-000000000000";
    this.GetNew = function() {
        var fourChars = function() {
            return (((1 + Math.random()) * 0x10000)|0).toString(16).substring(1).toUpperCase();
        }
        return (fourChars() + fourChars() + "-" + fourChars() + "-" + fourChars() + "-" + fourChars() + "-" + fourChars() + fourChars() + fourChars());
    };
})();

// usage example:
var tempId = PseudoGuid.GetNew();
someDomElement.id = tempId;

Это работает для меня, я только что проверил это в сценарии Greasemonkey сам.


ОБНОВЛЕНИЕ: Закрытия - это путь, которым я лично являюсь, как опытный разработчик JavaScript, я не знаю, как вы не сразу же подумали об этом. :)

myDomElement; // some DOM element we want later reference to

someOtherDomElement.addEventListener("click", function(e) {
   // because of the closure, here we have a reference to myDomElement
   doSomething(myDomElement);
}, false);

Теперь myDomElement - это один из элементов, который, по-видимому, у вас, по вашему описанию, уже есть (поскольку вы думали добавить к нему ID или что-то еще).

Возможно, если вы опубликуете пример того, что вы пытаетесь сделать, вам будет легче помочь, если это не так.

5 голосов
/ 23 октября 2008

ОБНОВЛЕНИЕ: Замыкания - действительно ответ. Поэтому, немного поигравшись с этим, я выяснил, почему замыкания изначально были проблематичными и как это исправить. Хитрость с замыканием заключается в том, что вы должны быть осторожны при переборе элементов, чтобы в итоге все замыкания не ссылались на один и тот же элемент. Например, это не работает:

for (var i = 0; i < elements.length; i++) {
    var element = elements[i];
    var button = document.createElement("button");
    button.addEventListener("click", function(ev) {
        // do something with element here
    }, false)
}

Но это так:

var buildListener = function(element) {
    return function(ev) {
        // do something with event here
    };
};

for (var i = 0; i < elements.length; i++) {
    var element = elements[i];
    var button = document.createElement("button");
    button.addEventListener("click", buildListener(element), false)
}

В любом случае, я решил не выбирать один ответ, потому что у вопроса было два ответа: 1) Нет, нет внутренних идентификаторов, которые вы можете использовать; 2) вы должны использовать замыкания для этого. Поэтому я просто проголосовал за первых людей, которые сказали, есть ли внутренние идентификаторы или кто рекомендовал создавать идентификаторы, плюс тех, кто упомянул закрытия. Спасибо за помощь!

4 голосов
/ 22 октября 2008

Если вы можете написать в DOM (я уверен, что вы можете). Я бы решил это так:

Функция возврата или генерации идентификатора:

//(function () {

  var idCounter = new Date().getTime();
  function getId( node ) {
    return (node.id) ? node.id : (node.id = 'tempIdPrefix_' + idCounter++ );
  }

//})();

Используйте это, чтобы получить ID при необходимости:

var n = document.getElementById('someid');
getId(n);  // returns "someid"

var n = document.getElementsByTagName('div')[1];
getId(n);  // returns "tempIdPrefix_1224697942198"

Таким образом, вам не нужно беспокоиться о том, как выглядит HTML, когда сервер передает его вам.

3 голосов
/ 22 октября 2008

Если вы не изменяете DOM, вы можете получить их все в индексированном порядке:

( Прототип пример)

myNodes = document.body.descendants()
alert(document.body.descendants()[1].innerHTML)

Вы можете перебрать все узлы и дать им уникальное имя класса, которое впоследствии сможете легко выбрать.

2 голосов
/ 22 октября 2008

Вы можете установить для атрибута id вычисленное значение. В библиотеке прототипов есть функция, которая может сделать это за вас.

http://www.prototypejs.org/api/element/identify

Моя любимая библиотека javascript - это jQuery. К сожалению, в jQuery нет функции, подобной идентификатору. Однако вы все равно можете установить для атрибута id значение, которое вы генерируете самостоятельно.

http://docs.jquery.com/Attributes/attr#keyfn

Вот частичный фрагмент из документов jQuery, который устанавливает id для элементов div на основе позиции на странице:

  $(document).ready(function(){

    $("div").attr("id", function (arr) {
          return "div-id" + arr;
        });
  });
1 голос
/ 19 мая 2017

Вы можете создать стабильный уникальный идентификатор для любого данного узла в DOM с помощью следующей функции:

function getUniqueKeyForNode (targetNode) {
    const pieces = ['doc'];
    let node = targetNode;

    while (node && node.parentNode) {
        pieces.push(Array.prototype.indexOf.call(node.parentNode.childNodes, node));
        node = node.parentNode
    }

    return pieces.reverse().join('/');
}

Это создаст идентификаторы, такие как doc/0, doc/0/0, doc/0/1, doc/0/1/0, doc/0/1/1 для такой структуры:

<div>
    <div />
    <div>
        <div />
        <div />
    </div>
</div>

Есть также несколько оптимизаций и изменений, которые вы можете сделать, например:

  • В цикле while, break, когда этот node имеет атрибут, который, как вы знаете, является уникальным, например, @id

  • Не reverse() pieces, в настоящее время он просто больше похож на структуру DOM, из которой генерируются идентификаторы

  • Не включайте первый piece doc, если вам не нужен идентификатор для узла документа

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

  • Если вы пишете эти идентификаторы обратно в XML, используйте другой символ конкатенации, если атрибут, который вы пишете, ограничен.

1 голос
/ 26 марта 2010

Используйте свойства мыши и / или положения элемента для создания уникального идентификатора.

1 голос
/ 24 октября 2008

Вы также можете использовать pguid (уникальный идентификатор страницы) для генерации уникального идентификатора:

 pguid = b9j.pguid.next() // A unique id (suitable for a DOM element)
                          // is generated
                          // Something like "b9j-pguid-20a9ff-0"
 ...
 pguid = b9j.pguid.next() // Another unique one... "b9j-pguid-20a9ff-1"

 // Build a custom generator
 var sequence = new b9j.pguid.Sequence({ namespace: "frobozz" })
 pguid = sequence.next() "frobozz-c861e1-0"

http://appengine.bravo9.com/b9j/documentation/pguid.html

...