JavaScript:如何在 Internet Explorer 中模拟更改事件(委托) [英] JavaScript: How to simulate change event in internet explorer (delegation)

查看:26
本文介绍了JavaScript:如何在 Internet Explorer 中模拟更改事件(委托)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

更新:(回顾、小提琴和赏金)

这个问题没有得到太多关注,所以我打算花一些时间在上面.我知道我的回答和问题都过于冗长.这就是为什么我继续设置这个小提琴,在我看来,这是一种不错的代表我目前不得不使用的代码来接近冒泡的 change 事件.我正在尝试解决的几个问题:

This question hasn't been getting too much attention, so I'm going to spend some rep on it. I know I tend to be overly verbose in both my answers and questions. That's why I went ahead and set up this fiddle, which is, in my view, a decent representation of the kind of code I'm currently having to use to come close to a bubbling change event. A couple of issues I'm trying to resolve:

  1. pseudo-change 事件不会在选择元素上触发,除非它失去焦点.在某些情况下,应在选择新值时重定向客户端.我如何实现这一目标?
  2. 点击标签和复选框本身时都会调用处理程序.这本身就是您所期望的,但是由于事件冒泡,(AFAIK)无法确定单击了哪个元素.IE 的事件对象没有 realTarget 属性.
  3. 当通过单击标签更改 IE 中复选框的 checked 状态时,一切正常(尽管它需要一些讨厌的解决方法),但是当直接单击复选框时,将调用处理程序,但选中状态保持不变,直到我再次单击.然后值会改变,但不会调用处理程序.
  4. 当我切换到不同的选项卡并再次返回时,处理程序会被多次调用.如果选中状态实际改变了三次,如果我只直接单击复选框一次,则两次.
  1. The pseudo-change event doesn't fire on a select element, unless it looses focus. In some cases the client should be redirected upon selecting a new value. How do I achieve this?
  2. The handler is called both when the labels are clicked, as well as the checkboxes themselves. In itself that's what you'd expect, but due to the event bubbling it's (AFAIK) impossible to determine which element was clicked. IE's event object doesn't have a realTarget property.
  3. When changing the checked-state of a checkbox in IE by clicking the label, all is well (though it requires some nasty workarounds), but when clicking the checkbox directly, the handler is called, but the checked state remains unchanged, until I click a second time. Then the value changes, but the handler isn't called.
  4. When I switch to a different tab, and back again, the handler is called multiple times. Three times if the checked state actually changed, twice if I clicked the checbox directly only once.

任何可以帮助我解决上述一个或多个问题的信息将不胜感激.拜托了,我没忘记加一个jQuery标签,我喜欢纯JS,所以我在找纯JS的答案.

Any information that could help me resolve one or more of the issues above would be greatly appreciated. Please, I didn't forget to add a jQuery tag, I like pure JS, so I'm looking for a pure JS answer.

我有一个网页,上面有超过 250 个选择元素和 20~30 个复选框.我还必须跟踪用户的行为,并采取适当的行动.因此,我很自然地委托更改事件,而不是添加数百个侦听器,恕我直言.

I've got a web page with well over 250 select elements on it, and 20~30 checkboxes. I also have to track the users' actions, and take appropriate actions. It is therefore quite natural for me to delegate the change event, rather then adding hundreds of listeners, IMHO.

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

Of course, IE -company policy: IE8 has to be supported- doesn't fire the onchange event when I need it. So I'm trying to fake an onchange event. What I have thus far is working reasonably well apart from 1 thing that really bugs me.
I'm using onfocusin and onfocusout to register the events. In some cases, when the user selects a new value from the a select element, the script should respond immediately. However, as long as the select hasn't lost focus, this won't happen.

这是我目前想到的:

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 事件.它触发了任何具有焦点 ATM 的选择的 onfocusout 事件.唯一的问题是,99.9% 的用户会点击选择,所以 focusin 事件也会触发 onclick.

I've tried attaching another event listener inside the onfocusin handler, bound to the onclick event. It fired the onfocusout event of whatever select has focus ATM. The only problem is, 99.9% of the users will click on a select, so the focusin event fires an onclick, too.

为了解决这个问题,我创建了闭包,并将当前的 select-in-focus 及其原始值作为参数传递给它.但是有些用户确实使用他们的键盘,并且这些用户通常在不更改值的情况下跳到下一个选择框.每次绑定一个新的 onclick 侦听器...我确实相信 HAS 比检查所有 e.type 并分别对待它们更简单.
举个例子:带有额外onclick监听器的代码:所有代码都与第一个片段相同,所以我只粘贴case 'select':

To get round that, I created closure, and passed the current select-in-focus and it's original value to it as arguments. But some users do use their keyboard, and these users often tab to the next select box without changing the value. Each time binding a new onclick listener... I do believe there HAS to be an easier way than to check all e.type's and treat them separately.
Just as an example: the code with an extra onclick listener: all code is the same as the first snippet, so I'm only pasting the case 'select': block

                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;

推荐答案

嗯,我有另一个破解方法,我想出了一个相当不错的方法(在工作中它成功了 - 我试图复制我写的代码,但经过几次啤酒后,它可能包含一些错误,但精神保持不变)

Well, I had another crack at it, and I've come up with a fairly decent approach (at work it did the trick - I've tried to replicate the code I wrote but after a few beers it might contain some errors but the spirit remains the same)

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 事件会触发两次.使用保存所有复选框实际选中状态的对象比单个处理程序成本更低,并且它允许我仅在元素的值更改后委托事件.

I've found out that the focusin events fire twice in some cases for radio's and checkboxes. Using an object that holds the actual checked states of all checkboxes is less expensive than individual handlers, and it allows me to only delegate the event after the value of the element has changed.

changeDelegator 函数仅在需要时调用,但我在此处发布的 anon 函数仍然比我想要的调用次数多 Waaaay,但这种方法仍然优于个别处理程序.

The changeDelegator function is only called when needed, but the anon function that I posted here still gets called Waaaay more than I wanted it, but this approach still outperforms the individual handlers-take.

我省略了选择,但我也让它们工作(类似的情况,在我的完整版本的代码中,闭包有 2 个对象,我做了它,所以我可以标记id,在需要时触发模糊事件,并重定向客户端).
在运行结束时,尽管我学到了一些新技巧,但我从这个练习中学到的主要东西是对IE这个可怕的、坚韧不拔的傀儡更加憎恨......但是,如果其他人可能想在 IE 中委托更改事件,请知道它(几乎)是可能的

I left out the selects, but I got them working, too (similar take, in the full version of my code the closure has 2 objects, and I made it, so I can flag an id, fire the blur event when needed, and the client is redirected).
At the end of the run, even though I've learned some new tricks, the main thing I take away from this exercise is an even more profound hatred of that ghastly, gritty golem of a thing called IE... But if anybody else might ever want to delegate change events in IE, know that it is (almost) possible

这篇关于JavaScript:如何在 Internet Explorer 中模拟更改事件(委托)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆