Уникальный идентификатор элемента, даже если у элемента его нет

StackOverflow https://stackoverflow.com/questions/226689

Вопрос

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

Редактировать:

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

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

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

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

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

Это было полезно?

Решение 4

Обновить: Замыкания действительно являются ответом.Итак, повозившись с этим еще немного, я понял, почему замыкания изначально были проблематичными и как это исправить.Сложность с замыканием заключается в том, что вы должны быть осторожны при переборе элементов, чтобы в конечном итоге все ваши замыкания не ссылались на один и тот же элемент.Например, это не работает:

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) для этого вы должны использовать замыкания.Поэтому я просто проголосовал за первых людей, которые сказали, существуют ли внутренние идентификаторы или кто рекомендовал генерировать идентификаторы, плюс за всех, кто упомянул о закрытии.Спасибо за помощь!

Другие советы

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

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

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

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

Закрытие - это правильный путь.Таким образом, у вас будет точная ссылка на элемент, который даже переживет некоторую перетасовку 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 для этой проблемы.

Немного смущает формулировка вашего вопроса - вы говорите, что вам "нужен идентификатор строки, который [вы] можете использовать для ссылки на этот элемент позже ", но что вы "не можете сохранить ссылки в [вашем] скрипте, потому что когда [вам] они понадобятся, сам скрипт 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 это один из элементов, которые, судя по вашему описанию, у вас, по-видимому, уже есть (поскольку вы подумывали добавить к нему идентификатор или что-то еще).

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

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

Есть функция, возвращающая или генерирующая идентификатор:

//(function () {

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

//})();

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

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

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

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

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

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

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

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

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

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

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

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

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

  $(document).ready(function(){

    $("div").attr("id", function (arr) {
          return "div-id" + arr;
        });
  });

Вы также можете использовать 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

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

Вы можете сгенерировать стабильный уникальный идентификатор для любого заданного узла в 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, используйте другой символ конкатенации, если записываемый вами атрибут ограничен.

В javascript вы могли бы прикрепить пользовательское поле идентификатора к узлу

if(node.id) {
  node.myId = node.id;
} else {
  node.myId = createId();
}

// store myId

Это немного халтурно, но это даст каждому узлу идентификатор, который вы можете использовать.Конечно, document.getElementById() не буду обращать на это внимания.

Я "думаю", что только что решил проблему, похожую на эту.Однако я использую jQuery в среде DOM браузера.

var objA = $("селектор для некоторого элемента dom");var objB = $("селектор на какой-либо другой элемент dom");

if( objA[0] === objB[0]) { //ОТЛИЧНО!два объекта указывают на точно такой же узел dom }

ОК, идентификатор, автоматически привязанный к элементу DOM, отсутствует.DOM имеет иерархическую структуру элементов, которая является основной информацией.С этой точки зрения, вы можете связывание данных с элементами DOM с помощью jQuery или jqLite.Это может решить некоторые проблемы, когда вам приходится привязывать пользовательские данные к элементам.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top