JavaScript: как имитировать событие изменения в internet explorer (делегирование)

обновление: (резюме, скрипка и щедрость)

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

  1. на pseudo-change событие не срабатывает на элементе select, если оно не теряет фокус. В некоторых случаях клиент должен быть перенаправлен при выборе нового значения. Как мне этого достичь?
  2. обработчик вызывается как при щелчке меток, так и самих флажков. Само по себе это то, что вы ожидаете, но из-за события пузырящегося (AFAIK) невозможно определить, какой элемент был нажат. Объект события IE не имеет realTarget свойство.
  3. при изменении checked - состояние флажка в IE, нажав на метку, все хорошо (хотя это требует некоторых неприятных обходных путей), но при нажатии на флажок напрямую вызывается обработчик, но проверенное состояние остается неизменным, пока я не нажму второй раз. Затем значение изменяется, но обработчик не вызывается.
  4. когда я переключиться на другую вкладку и обратно, обработчик вызывается несколько раз. Три раза, если государство на самом деле изменено, дважды, если я щелкнул checbox напрямую только один раз.

любая информация, которая может помочь мне решить один или несколько из вышеперечисленных вопросов, была бы весьма признательна. Пожалуйста, я не забыл добавить jQuery tag, мне нравится чистый JS, поэтому я ищу чистый ответ JS.


у меня есть веб-страница с более чем 250 элементами выбора на ней и 20~30 флажками. Я также должен отслеживать действия пользователей и принимать соответствующие меры. Он поэтому вполне естественно для меня делегировать событие изменения, а не добавлять сотни слушателей, ИМХО.

конечно, IE-политика компании: IE8 должен поддерживаться - не запускает событие onchange, когда мне это нужно. Поэтому я пытаюсь подделать событие onchange. То, что у меня до сих пор работает достаточно хорошо, кроме 1 вещи, которая действительно меня беспокоит.
я использую onfocusin и onfocusout Регистрация событий. В некоторых случаях, когда пользователь выбирает новое значение из выбора элемент, сценарий должен ответить немедленно. Однако, пока выбор не потерял фокус, этого не произойдет.

вот что я придумал до сих пор:

i = document.getElementById('content');
if (!i.addEventListener)
{
    i.attachEvent('onfocusin',(function(self)
    {
        return function(e)
        {
            e = e || window.event;
            var target = e.target || e.srcElement;
            switch (target.tagName.toLowerCase())
            {
                case 'input':
                    if (target.getAttribute('type') !== 'checkbox')
                    {
                        return true;
                    }
                    return changeDelegator.apply(self,[e]);//(as is
                case 'select':
                    self.attachEvent('onfocusout',(function(self,current)
                    {
                        return function(e)
                        {
                            e = e || window.event;
                            var target = e.target || e.srcElement;
                            if (target !== current)
                            {
                                return;
                            }
                            self.detachEvent('onfocusout',arguments.callee);//(remove closure
                            return changeDelegator.apply(self,[e]);
                        }
                    })(self,target));
                default: return;
            }
        }
    })(i));//(fake change event, buggy
}
else
{//(to put things into perspective: all major browsers:
    i.addEventListener('change',changeDelegator,false);
}

я попытался подключить другой прослушиватель событий внутри onfocusin обработчик, привязанный к onclick событие. Он выстрелил onfocusout событие любого выбора имеет фокус ATM. Проблема в том, что 99.9% пользователей будут нажимать на выбор, так что focusin событие также запускает onclick.

для обойдя это, я создал закрытие и передал текущий select-in-focus и его исходное значение в качестве аргументов. Но некоторые пользователи do используйте свою клавиатуру, и эти пользователи часто вкладывают в следующее поле выбора без изменения значения. Каждый раз привязка нового прослушивателя onclick... Я верю в это и чтобы быть проще, чем проверить все e.typeи лечить их отдельно.
просто в качестве примера: код с дополнительным onclick слушатель: весь код то же самое, что и первый фрагмент, поэтому я только вставляю case 'select': блок

                case 'select':
                    self.attachEvent('onfocusout',(function(self,current)
                    {
                        return function(e)
                        {
                            e = e || window.event;//(IE...
                            var target = e.target || e.srcElement;
                            if (!(target === current))
                            {
                                return;
                            }
                            self.detachEvent('onfocusout',arguments.callee);//(remove closure
                            return changeDelegator.apply(self,[e]);
                        };
                    })(self,target));
                    self.attachEvent('onclick',(function(self,current,oldVal)
                    {
                        return function(e)
                        {
                            e = e || window.event;
                            var target = e.target || e.srcElement;
                            if (target.tagName.toLowerCase() === 'option')
                            {
                                while(target.tagName.toLowerCase() !== 'select')
                                {
                                    target = target.parentNode;
                                }
                            }
                            if (target !== current)
                            {//focus lost, onfocusout triggered anyway:
                                self.detachEvent('onclick',arguments.callee);
                                return;
                            }
                            var val = target.options[target.selectedIndex].innerHTML;//works best in all browsers
                            if (oldVal !== target.options[target.selectedIndex].innerHTML)
                            {
                                self.detachEvent('onclick',arguments.callee);
                                return target.fireEvent('onfocusout');
                            }
                        };
                    })(self,target,target.options[target.selectedIndex].innerHTML));
                default: return;

2 ответов


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

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

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

для решения ваших вопросов a бит:

  1. вы уверены в этом? Я пытался основной пример из документации Microsoft, и оба IE7 и IE8 огонь onchange слушатель после того, как я нажму на опцию из раскрывающегося списка. Он даже срабатывает при изменении выбора с помощью клавиш up/down.

  2. а это правда, что вы не можете легко подключите событие на метке к соответствующему флажку, почему вы хотите это сделать? The change событие будет инициировано на флажке в любом случае, и вы должны заботиться только об этом. Однако, если вы должны добраться до флажка из события на метке, вы можете сделать это вручную. Если событие нацелено на метку, то вы знаете, что метка каким-то образом связана с вводом. В зависимости от того, как вы используете метки, Вы можете выбрать input элемент внутри label, или получить элемент с идентификатором найден в из label.

  3. один из способов исправления флажки возвращают Предыдущее значение при изменении ошибка-это сделать this.blur() on focus событий на флажки.

  4. когда вы говорите "обработчик", вы имеете в виду onfocusin обработчик событий, или changeDelegator? Это нормально видеть focusin пожар события при повторной активации вкладки. Я не знаю точно, почему событие запускается более одного раза, поэтому я просто предполагаю: один может быть фокусом, что активный ввод получает; второй может быть фокусом, который получает сам документ; я понятия не имею, почему происходит третий вызов. Я не понимаю, что вы подразумеваете под " Если проверенное состояние действительно изменилось, [...] если я щелкнул флажок непосредственно только один раз".


Ну, у меня была еще одна трещина, и я придумал довольно приличный подход (на работе он сделал трюк - я попытался скопировать код, который я написал, но после нескольких пива он может содержать некоторые ошибки, но дух остается прежним)

window.attachEvent('onload',function ieLoad()
{
    var mainDiv = document.getElementById('main');//main div, from here events will be delegated
    var checks = mainDiv.getElementsByTagName('input');//node list of all inputs
    var checkStates = {};
    for (var i=0;i<checks.length;i++)
    {
        if (checks[i].type === 'checkbox')
        {//get their checked states on load, this object serves as a reference
            checkStates[checks[i].id] = checks[i].checked;
        }
    }
    mainDiv.attachEvent('onfocusin',(function(initState)
    {//initState holds a reference to the checkStates object
        return function(e)
        {
            e = e || window.event;
            var target = e.target || e.srcElement;
            //id of checkboxes used as key, so no checking for tagName or type required
            if (!initState.hasOwnProperty(target.id) || target.checked === initState[target.id])
            {//don't call method if checkstate is unchanged, either. I'll explain in a minute
                return e;
            }
            initState[target.id] = target.checked;//set new checked-state
            changeDelegator.apply(target,[e]);//delegate
        };
    })(checkStates));
    window.detachEvent('onload',ieLoad);//avoid mem-leak with onload handler!
});

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

на changeDelegator функция вызывается только при необходимости, но функция anon, которую я разместил здесь, по-прежнему называется Waaaay больше, чем я хотел, но этот подход по-прежнему превосходит отдельных обработчиков-возьмите.

Я оставил выбор, но я получил их работу тоже (аналогично, в полной версии моего кода закрытие имеет 2 объекта, и я сделал это, поэтому я могу флаг идентификатор, запускает событие размытия, когда это необходимо,и клиент перенаправляется). почти) можно