Уникальный идентификатор элемента, даже если у элемента его нет
-
03-07-2019 - |
Вопрос
Я пишу скрипт 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"
Используйте мышь и / или позиционные свойства элемента для создания уникального идентификатора.
Вы можете сгенерировать стабильный уникальный идентификатор для любого заданного узла в 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.Это может решить некоторые проблемы, когда вам приходится привязывать пользовательские данные к элементам.