Передача события jquery по значению - PullRequest
1 голос
/ 26 июля 2011

На следующей странице отображаются пять кнопок, при нажатии каждой кнопки появляется предупреждение «5».Я хочу, чтобы, нажимая первую кнопку, я получал предупреждение «1», вторая кнопка «2» ... и т. Д.

<!doctype html>
<html>
    <head>
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
    </head>
    <body>
        <div id="nums"></div>
        <script type="text/javascript">
            var nums = [1,2,3,4,5];
            for(var i in nums){
                var num = nums[i];
                var btn=$('<button></button>').text(num).click(function(){alert(num);});
                $('#nums').append(btn);
            }
        </script>
    </body>
</html>

Ответы [ 6 ]

5 голосов
/ 26 июля 2011

JavaScript имеет только область действия функции, поэтому в вашем примере есть только один num, который постоянно изменяется, и к моменту вызова события click он равен пяти.Чтобы обойти это, вы должны создать правильное замыкание с новой областью действия, например:

var nums = [1,2,3,4,5];
var createClick = function (num) { return function() { alert(num); }; };
for(var i in nums){
    var num = nums[i];
    var btn=$('<button></button>').text(num).click(createClick(num));
    $('#nums').append(btn);
}  
3 голосов
/ 26 июля 2011

Функции обработчика событий, которые вы создаете в цикле, имеют устойчивую ссылку на переменные в области видимости, в которой они созданы, а не копия этих переменных на момент, когда функция была создана. Вот как работают замыкания (подробнее: Замыкания не сложны ). Таким образом, все ваши функции-обработчики ссылаются на одну и ту же переменную num, и они ссылаются на нее, когда происходит событие click. Поскольку в этот момент это 5 (последнее значение, которое ему когда-либо было назначено), все вы получите предупреждение 1011 *.

Если вы хотите, чтобы обработчик событий ссылался на num на момент создания обработчика, вы должны закрыть обработчик над чем-то, что не изменится. Обычный способ состоит в том, чтобы иметь функцию, которая строит вашу функцию-обработчик, и иметь функцию-обработчик, близкую к аргументу этой функции-построителя:

var nums = [1,2,3,4,5];
for(var i in nums){
    var num = nums[i];
    var btn=$('<button></button>').text(num).click(buildHandler(num));
    $('#nums').append(btn);
}

function buildHandler(val) {
    return function(){alert(val);};
}

Как видите, мы вызываем buildHandler при подключении события click и передаем его возвращаемое значение в click. buildHandler создает и возвращает функцию, которая закрывает val, аргумент, который мы передаем ей. Поскольку val является копией номера, о котором мы хотим, чтобы он предупреждал, и ничто не изменится val, функции будут работать так, как нужно.


Не по теме 1 : Вы создаете много глобальных переменных в этом коде. Глобальное пространство имен уже очень переполнено, я рекомендую избегать создания большего числа глобальных переменных всякий раз, когда вы можете избежать этого. Я бы обернул этот код в функцию, чтобы все переменные были локальными для этой функции:

<script>
    (function() {
        var nums = [1,2,3,4,5];
        for(var i in nums){
            var num = nums[i];
            var btn=$('<button></button>').text(num).click(buildHandler(num));
            $('#nums').append(btn);
        }

        function buildHandler(val) {
            return function(){alert(val);};
        }
    })();
</script>

Он делает то же самое (мы определяем функцию, затем вызываем ее немедленно), но без создания глобальных переменных i, num, nums и btn.


Не по теме 2 : вы использовали for..in с массивом без каких-либо проверок, чтобы убедиться, что вы имеете дело только с индексами массива. for..in делает не цикл по индексам массива; он перебирает имена свойств объекта. Написание циклов for..in так, как если бы они проходили по индексам массивов, в какой-то момент укусит вас. Просто используйте обычный цикл for или выполните необходимые проверки или используйте функцию jQuery each. Подробнее: Мифы и реалии for..in

2 голосов
/ 26 июля 2011

Все создаваемые вами обработчики click ссылаются на одну и ту же глобальную переменную num, которая будет 5 после запуска цикла и еще пять в точке, когда вы действительно нажимаете, чтобы вызвать обработчик и отобразить предупреждение.

Два способа обойти это следующим образом:

(1) Поскольку вы уже устанавливаете число в качестве текста кнопки, используйте его в обработчике следующим образом:

// jQuery will set 'this' for you when it calls your click handler
[...].click(function(){ alert($(this).text()); });

(2) Создайте замыкание, чтобы отдельные значения num сохранялись для каждого обработчика:

[...].click((function(closureNum){
                return function(){ alert(closureNum); }
             })(num));

РЕДАКТИРОВАТЬ: Просто видел другие ответы. Для лучшего варианта моего второго варианта я бы порекомендовал делать то, что T.J. и FishBasketGordo сделал и имеет отдельную функцию построения обработчика, а не встроенную, как я. Любой из них должен работать, но отдельный построитель обработчиков должен быть более эффективным (и, возможно, более простым для чтения).

2 голосов
/ 26 июля 2011

Изменить эту строку:

var btn=$('<button></button>').text(num).click(function(){alert($(this).text()});
0 голосов
/ 26 июля 2011

Это проще:

var nums = [1,2,3,4,5];
$.each(nums, function(index, value){
    var btn=$('<button></button>').text(value).click(function(){alert(value);});
    $('#nums').append(btn);
});

Поскольку у вас уже есть jquery на вашей странице, почему бы не использовать $ .each ()?

0 голосов
/ 26 июля 2011

Попробуйте это

<script type="text/javascript">
            var nums = [1,2,3,4,5];
            for(var i in nums){
                var num = nums[i];
                var btn=$('<button></button>').text(num).click(function(){alert(nums[i]);});
                $('#nums').append(btn);
            }
        </script>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...