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

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

问题描述

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

这个问题没有得到太多注意,所以我要花一些代表。我知道我的答案和问题往往过于冗长。这就是为什么我继续设置这个小提琴,在我看来,这是一种不错的表现形式代码我目前不得不用来接近冒泡的更改事件。我正在尝试解决的几个问题:

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. 伪更改事件不会触发选择元素,除非它失去焦点。在某些情况下,应在选择新值时重定向客户端。我如何实现这一目标?

  2. 在单击标签时调用处理程序,以及复选框本身。这本身就是你所期望的,但由于事件冒泡,它(AFAIK)无法确定点击了哪个元素。 IE的事件对象没有 realTarget 属性。

  3. 更改选中
  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 -company政策:IE8必须得到支持 - 在我需要时不会触发onchange事件。所以我试图假装一个onchange事件。到目前为止,我所做的工作相当不错,除了一件真正让我烦恼的事情。
我正在使用 onfocusin onfocusout 来注册事件。在某些情况下,当用户从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.

这是我到目前为止所提出的:

Here's what I came up with so far:

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。

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 和tre更容易在他们分开。
仅作为示例:带有额外 onclick 监听器的代码:所有代码都与第一个代码段相同,所以我只粘贴案例'选择':

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,在需要时触发blur事件,并重定向客户端) 。在运行结束时,尽管我已经学会了一些新的技巧,但我从这次练习中拿走的主要内容却是对一种叫做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天全站免登陆