#javascript #html #dom
#javascript #HTML #dom
Вопрос:
Я пытаюсь заменить все текстовые поля на странице метками.
function replaceInputTextFieldsWithValues() {
var inputFields = document.getElementsByTagName("input");
for(var i = 0; i < inputFields.length; i ) {
if(inputFields[i].getAttribute("type")== "text") {
var parent = inputFields[i].parentNode;
var value = inputFields[i].value;
parent.removeChild(inputFields[i]);
var label = document.createElement('label');
label.setAttribute('for', value);
label.innerHTML = value;
parent.appendChild(label);
}
}
}
Мой HTML-документ организован в таблицах. Кажется, что эта функция работает только с первым элементом в каждой таблице.
С другой стороны, когда я удаляю строку:
parent.removeChild(inputFields[i]);
Код, похоже, работает нормально. Почему это происходит и как мне это исправить?
Комментарии:
1. Атрибут ‘for’ предназначен для идентификатора поля ввода, так что фокус на метке активирует связанное с ним поле. Строго говоря, метка без поля недопустима. Удаление входных данных не дает возможности ничего делать. Возможно, вам следует пропустить настройку for, или скрыть ввод, или даже использовать другой тип элемента, кроме label.
2. @kennebec: Очень хорошая мысль, я должен признать, что даже не дочитал до конца. @BlackSheep: Ни в коем случае не используйте
label
для этого, а если используете, то не используйте егоfor
атрибут. Используйте span сdata-val
атрибутом или чем-то еще (пользовательские атрибуты сdata-
префиксом работают прямо сейчас во всех основных браузерах и действительны начиная с HTML5).
Ответ №1:
То, что вы получаете в ответ, getElementsByTagName
это HTMLCollection
живой файл. (Это верно для других getElementsByXYZ
методов, но не querySelectorAll
.) Это означает, что если вы удалите элемент с индексом 0
, его HTMLCollection
длина уменьшится, и у вас будет новый элемент с индексом 0
вместо того, который вы только что удалили.
Просто проделайте свой путь в обратном направлении, и все будет в порядке:
for(var i = inputFields.length - 1; i >= 0; i--) {
// ...
}
В качестве альтернативы преобразуйте HTMLCollection
в массив, а затем выполняйте цикл по массиву. (Смотрите живой пример и код ниже).
Редактировать: Или, как указывает Крис Шаутс в комментариях, вы можете просто воспользоваться изменением length
, но это не совсем так просто, как предлагает Крис, потому что вы удаляете элементы только иногда. Это выглядело бы так:
var inputFields = document.getElementsByTagName("input");
var i = 0;
while (i < inputFields.length) {
if(inputFields[i].getAttribute("type")== "text") {
// Remove it and DON'T increment `index`
}
else {
// Skip this one by incrementing `index`
index;
}
}
Какой из этих трех подходов использовать, будет зависеть от ситуации. Копирование в массив дает вам хороший статический набор данных для работы, и если вы убедитесь, что выпустили ссылку на HTMLCollection
, вы даете браузеру возможность понять, что ему не нужно обновлять этот список при изменении ситуации, что может снизить накладные расходы. Но вы копируете ссылки кратко, что немного увеличивает накладные расходы. 🙂
Дополнительно: Вот пример, показывающий этот эффект, а также демонстрирующий довольно эффективный (но малопонятный) способ создания массива из HTMLCollection
:
HTML:
<ul>
<li>LI0</li>
<li>LI1</li>
<li>LI2</li>
</ul>
JavaScript:
var lilist, liarray;
// Get the HTMLCollection, which is live
lilist = document.getElementsByTagName('li');
// Create an array of its elements
liarray = Array.prototype.slice.call(lilist, 0);
// Show initial length of both
display("lilist.length = " lilist.length); // Shows 3
display("liarray.length = " liarray.length); // Shows 3
// Show what the 0th element of both is (both show "LI0" in the live example)
display("lilist[0].innerHTML = " lilist[0].innerHTML); // Shows LI0
display("liarray[0].innerHTML = " liarray[0].innerHTML); // Shows LI0
// Remove the first list item
display("Removing item 0");
lilist[0].parentNode.removeChild(lilist[0]);
// Show the length of both, note that the list's length
// has gone down, but the array's hasn't
display("lilist.length = " lilist.length); // Shows 2, not 3
display("liarray.length = " liarray.length); // Still shows 3
// Show what the 0th element of both *now* is
display("lilist[0].innerHTML = " lilist[0].innerHTML); // Shows LI1 now
display("liarray[0].innerHTML = " liarray[0].innerHTML); // Still shows LI0
Комментарии:
1. В качестве альтернативы, если вы не хотите выполнять цикл в обратном направлении, вы всегда можете сделать
while(inputFields.length >= 0)
и всегда работать сinputFields[0]
внутри цикла.2. @Chris: В целом это хорошая мысль, но у него есть некоторая логика в отношении того, удалять
input
или нет, и поэтому ему понадобится индекс, который он увеличивал при пропуске, но не делал при удалении, а затем ему нужно будет проверить это наlength
, и это становится (немного) сложнее. Не очень сложный (я уверен, что мы оба это делали), но сложный.