Javascript - динамически назначать событие onclick в цикле
У меня очень простая HTML-страница с кодом JS:
<html>
<head>
<title></title>
</head>
<body>
<div id="divButtons">
</div>
<script type="text/javascript">
var arrOptions = new Array();
for (var i = 0; i < 10; i++) {
arrOptions[i] = "option" + i;
}
for (var i = 0; i < arrOptions.length; i++) {
var btnShow = document.createElement("input");
btnShow.setAttribute("type", "button");
btnShow.value = "Show Me Option";
var optionPar = arrOptions[i];
btnShow.onclick = function() {
showParam(optionPar);
}
document.getElementById('divButtons').appendChild(btnShow);
}
function showParam(value) {
alert(value);
}
</script>
</body>
</html>
Эта страница связывает 10 кнопок, но когда вы нажимаете любую кнопку, она всегда показывает предупреждение "option9". Как можно назначить событие onclick для показа соответствующей опции!?
Спасибо!
8 ответов
Вам придется сделать что-то вроде этого:
btnShow.onclick = (function(opt) {
return function() {
showParam(opt);
};
})(arrOptions[i]);
Примите во внимание тот факт, что при выполнении функции onclick() все, что она имеет:
showParam(optionPar);
дословно. OptionPar будет разрешен во время выполнения события click, и в этот момент он, скорее всего, будет последним значением, которое вы ему присвоили. Как правило, вы должны избегать передачи переменных таким способом.
Проблему, которую вы пытаетесь решить, лучше всего решить, переписав такую часть, как:
btnShow.value = "Show Me Option";
var optionPar = arrOptions[i];
btnShow.optionPar = optionPar;
btnShow.onclick = function(e) {
// if I'm not mistaking on how to reference the source of the event.
// and if it would work in all the browsers. But that's the idea.
showParam(e.source.optionPar);
}
Принятый ответ, кажется, работает, но, кажется, сбивает с толку и несколько обременительный способ сделать это. Возможно, лучшим способом будет использование атрибута данных для элемента, для которого вы хотите назначить прослушиватель событий. Это просто, легко понять и намного меньше кода. Вот пример:
btnShow.data = arrOptions[i];
btnShow.onclick = function() {
showParam(this.data);
}
Принятый ответ правильный, но я чувствую, что никакого реального объяснения не было сделано.
Позвольте мне попытаться объяснить, проблема здесь в классическом пропущенном закрытии.
Переменная 'i' увеличивается на 1 за каждую итерацию цикла, и событие щелчка фактически не выполняется, независимо от того, применяется ли оно к элементу a, оно суммируется до длины arrOptions, равной 10.
Таким образом, цикл продолжается до тех пор, пока 'i' не станет 10, а затем, всякий раз, когда вызывается событие нажатия, оно принимает значение i, равное 10.
Теперь для решения в решении мы используем замыкание, поэтому, когда мы применяем значение 'i' к событию нажатия элемента a, оно фактически получает точное значение i в момент времени.
Внутренняя функция события onclick создает замыкание, где оно ссылается на параметр (arrOptions[i]), означающий, что фактическая переменная i находится в нужное время.
Функция в конце концов безопасно закрывается с этим значением и может затем вернуть соответствующее значение при выполнении события нажатия.
Я прикрепляю обработчик события:
window.onload = function() {
var folderElement;
tagFolders = document.getElementById("folders");
for (i = 0; i < folders.length; i++) {
folderElement = folderButtons[i];
folderElement = document.createElement("button");
folderElement.setAttribute("id", folders[i]);
folderElement.setAttribute("type", "button");
folderElement.innerHTML = folders[i];
if (typeof window.addEventListener !== "undefined") {
folderElement.addEventListener("click", getFolderElement, false);
} else {
folderElement.attachEvent("onclick", getFolderElement);
}
tagFolders.appendChild(folderElement);
}
который может извлечь что угодно из элемента, который вызвал событие:
// This function is the event handler for the folder buttons.
function getFolderElement(event) {
var eventElement = event.currentTarget;
updateFolderContent(eventElement.id);
}
в этом случае вы должны встроить опцию в элемент / тег. В моем случае я использую идентификатор.
Для jquery, посмотрите раздел добавления данных о событиях из API:
...
for (var i = 0; i < arrOptions.length; i++) {
$('<input id="btn" type="button" value="Show Me Option"><input>').appendTo("#divButtons")
$('#btn').bind("click", {
iCount: i},
function(event) {
showParam(arrOptions[iCount]);
});
}
pp();
function pp()
{
for(j=0;j<=11;j++)
{
if(j%4==0)
{
html+= "<br>";
}
html += "<span class='remote' onclick='setLift(this)' >"+ j+"</span>";
}
document.getElementById('el').innerHTML = html;
}
function setLift(x)
{
alert(x.innerHTML);
}
Вы передаете в функцию только ссылку на переменную, а не ее значение. Поэтому каждый раз, когда цикл повторяется, он назначает ссылку на вашу анонимную функцию, и все они указывают на одно и то же значение в памяти. Но так как вы используете одно и то же имя переменной в цикле, вы перезаписываете значение переменной. Вы можете объединить переменную в строку, чтобы сохранить ее значение. Например, вот так:
btnShow.onclick = new Function("", "showParam(" + arrOptions[i] + ");");
Первый параметр - это имя функции, но это не обязательно (его можно оставить пустым или вообще не указывать).