JavaScript:如何在internet explorer中模拟更改事件(委派)

JavaScript:如何在internet explorer中模拟更改事件(委派),javascript,internet-explorer,dom-events,event-delegation,Javascript,Internet Explorer,Dom Events,Event Delegation,更新:(重述、提琴和赏金) 这个问题一直没有得到太多的关注,所以我将花一些时间来讨论它。我知道我的回答和问题都过于冗长。这就是为什么我继续进行设置,在我看来,这是一种很好的代码表示形式,我目前必须使用这种代码来接近冒泡的change事件。我正试图解决几个问题: pseudo change事件不会在select元素上触发,除非它失去焦点。在某些情况下,应在选择新值时重定向客户端。我如何做到这一点 该处理程序在单击标签时被调用,并且复选框本身也被调用。这本身就是您所期望的,但是由于事件冒泡,因此(A

更新:(重述、提琴和赏金)

这个问题一直没有得到太多的关注,所以我将花一些时间来讨论它。我知道我的回答和问题都过于冗长。这就是为什么我继续进行设置,在我看来,这是一种很好的代码表示形式,我目前必须使用这种代码来接近冒泡的
change
事件。我正试图解决几个问题:

  • pseudo change
    事件不会在select元素上触发,除非它失去焦点。在某些情况下,应在选择新值时重定向客户端。我如何做到这一点
  • 该处理程序在单击标签时被调用,并且复选框本身也被调用。这本身就是您所期望的,但是由于事件冒泡,因此(AFAIK)无法确定单击了哪个元素。IE的事件对象没有
    realTarget
    属性
  • 当通过单击标签更改IE中复选框的
    选中状态时,一切正常(尽管需要一些棘手的解决方法),但当直接单击复选框时,将调用处理程序,但选中状态保持不变,直到我再次单击为止。然后值会更改,但不会调用处理程序
  • 当我切换到另一个选项卡并再次返回时,会多次调用处理程序。如果checked状态实际发生了更改,则会更改三次,如果我只直接单击了一次checbox,则会更改两次 任何能帮助我解决上述一个或多个问题的信息都将不胜感激。请注意,我没有忘记添加jQuery标记,我喜欢纯JS,所以我正在寻找纯JS的答案


    我有一个网页,上面有超过250个选择元素,还有20~30个复选框。我还必须跟踪用户的行为,并采取适当的措施。因此,我很自然地授权变更事件,而不是添加数百个侦听器,IMHO

    当然,IE-公司政策:必须支持IE8-在需要时不会触发onchange事件。所以我想假装一个onchange事件。到目前为止,除了一件真正让我烦恼的事情外,我的工作还算不错
    我正在使用
    onfocusin
    onfocusout
    来注册事件。在某些情况下,当用户从select元素中选择新值时,脚本应该立即响应。但是,只要选择没有失去焦点,这种情况就不会发生

    以下是我到目前为止的想法:

    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
    事件。它触发了任何select有焦点的ATM的
    onfocusout
    事件。唯一的问题是,99.9%的用户会单击一个select,因此
    focusin
    事件也会触发onclick

    为了解决这个问题,我创建了闭包,并将焦点中的当前select及其原始值作为参数传递给它。但有些用户确实使用键盘,这些用户通常在不更改值的情况下切换到下一个选择框。每次绑定新的onclick侦听器时。。。我相信一定有比检查所有
    e更简单的方法。键入
    ,然后分别处理它们
    举个例子:带有一个额外的
    onclick
    listener的代码:所有代码都与第一个代码片段相同,因此我只粘贴
    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;
    

    虽然我同意在整个表单上只有一个事件监听器比有多个监听器更好,每个元素一个监听器,但您必须评估您的决策的成本和收益。一个侦听器的好处是减少了内存占用。缺点是,您必须使用复杂的代码技巧来绕过浏览器错误的事件实现、增加执行时间、误用事件类型、反复注册和注销事件侦听器,这可能会给某些用户造成混乱

    回到好处上来,如果您只为所有元素附加与侦听器相同的函数,那么内存占用就不会太大。内存是当前计算机所不缺少的

    即使这不能回答您的问题,我还是建议您停止尝试使其工作,而是在每个表单元素上附加侦听器

    要稍微说明你的观点:

  • 你确定吗?我已经尝试过了,当我从下拉列表中单击一个选项后,IE7和IE8都会启动
    onchange
    侦听器。它甚至在使用向上/向下键更改选择时激发

  • 确实,您不能轻松地将标签上的事件连接到受影响的复选框,但您为什么要这样做?无论如何,
    change
    事件都会在复选框上触发,您应该只关注该事件。但是,如果必须从标签上的事件访问复选框,则可以手动执行此操作。如果事件的目标是一个标签,那么您就知道该标签以某种方式与输入相关。根据标签的使用方式,您可以选择嵌套在
    标签
    中的
    输入
    元素,或者获取ID位于
    标签
    属性中的元素

  • 修复复选框在更改错误时返回上一个值的一种方法是对复选框上的事件执行
    this.blur()

  • 当您说“handler”时,您是指事件处理程序中的
    onfocusin,还是
    changeDelegator
    ?重新激活选项卡时,在
    事件中看到聚焦是正常的。我不知道为什么会多次触发事件,所以我只是猜测:一个可能是活动输入接收到的焦点;第二个可能是文档本身接收到的焦点;我不知道为什么会有第三个电话。我不明白w
    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!
    });