关于如何更好的监听元素属性的变化(转)

我们都知道,jQuery有一个onchange的事件来判断类似input或者textarea标签值变化的事件。

jQuery中是通过比如keyup,blur,click等事件来监听值的变化,如果变化就触发change事件。

jQuery.event.special.change = {
        filters: {
            focusout: testChange,
 
            beforedeactivate: testChange,
 
            click: function( e ) {
                var elem = e.target, type = elem.type;
 
                if ( type === "radio" || type === "checkbox" || elem.nodeName.toLowerCase() === "select" ) {
                    testChange.call( this, e );
                }
            },
 
            // Change has to be called before submit
            // Keydown will be called before keypress, which is used in submit-event delegation
            keydown: function( e ) {
                var elem = e.target, type = elem.type;
 
                if ( (e.keyCode === 13 && elem.nodeName.toLowerCase() !== "textarea") ||
                    (e.keyCode === 32 && (type === "checkbox" || type === "radio")) ||
                    type === "select-multiple" ) {
                    testChange.call( this, e );
                }
            },
 
            // Beforeactivate happens also before the previous element is blurred
            // with this event you can't trigger a change event, but you can store
            // information
            beforeactivate: function( e ) {
                var elem = e.target;
                jQuery._data( elem, "_change_data", getVal(elem) );
            }
        },
 
        setup: function( data, namespaces ) {
            if ( this.type === "file" ) {
                return false;
            }
 
            for ( var type in changeFilters ) {
                jQuery.event.add( this, type + ".specialChange", changeFilters[type] );
            }
 
            return rformElems.test( this.nodeName );
        },
 
        teardown: function( namespaces ) {
            jQuery.event.remove( this, ".specialChange" );
 
            return rformElems.test( this.nodeName );
        }
    };

YUI3中也有一个模块,叫做value-change模块,YUI3中使用了一个50毫秒的定时器来定期检查value的变化

其实一开始的时候我觉得很纳闷,为什么浏览器原生有对值变化进行监听的事件,而yui和jquery却都不采用呢?

先说说原生的解决方案:

ie下有onpropertychange,onchange两个事件来监听一个元素下属性的变化

onchange的介绍

Fires when the contents of the object or selection have changed.

详细:http://msdn.microsoft.com/en-us/library/ms536912(v=VS.85).aspx

例子:http://samples.msdn.microsoft.com/workshop/samples/author/dhtml/refs/onchangeEX.htm

实际上onchange却是在blur,也就是失去焦点的时候才会触发,这明显不是我们想要的

那么,onpropertychange呢?

Fires when a property changes on the object.

详细介绍:http://msdn.microsoft.com/en-us/library/ms536956(v=VS.85).aspx

例子:http://samples.msdn.microsoft.com/workshop/samples/author/dhtml/refs/onpropertychangeEX.htm

嗯,或许在ie下,onpropertychange更合适,可以直接在改变值的时候进行触发。

除了ie之外的浏览器上,解决方案就百花齐放了

首先,firefox有自己的一个watch方法,使用方法就是el.watah(“property”, callback),这个watch可以用于监听所有对象类型的属性改变,详细在https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/watch

比较通用的一个方法是input事件,在除了ie之外的浏览器都支持这个事件,不过现在的ie9也开始支持input事件了,这个事件和onpropertychange不一样的是input事件只监听内容文本的变化,也就是对value值的改变

Occurs when the text content of an element is changed through the user interface.

因为对value值的改变实际上也是对dom树的改变(dom树不仅仅是结构,也包括它的内容和属性),所以我们可以用dom level 2的 mutation event,比如DOMAttrModified,具体可以查看http://www.w3.org/TR/DOM-Level-3-Events/#event-type-DOMAttrModified,也可以阅读https://developer.mozilla.org/En/XUL/Events#Mutation%5FDOM%5Fevents,现在,chrome和safari对这些事件有比较好的支持

那么,我们回到之前的问题,为什么jQuery和YUI都没有选择使用这些浏览器原生提供的接口,而是另辟蹊径呢?

MDN里这么写的:

It should be noted that the presence of any mutation event listeners in a document degrades the performance of subsequent DOM operations in that document. In simple tests that can mean that a DOM operation takes between 1.5 and 7 as long as it would without any mutation listener. More information can be found in the “performance impact of DOM mutation listeners” threadin mozilla.dev.platform.

那么,它给的链接里有什么呢?

Boris Zbarsky这位同学把这个问题解释了一下:

Mutation listeners cause slowdown in two ways:

1)  Firing the event takes time O(tree depth at which mutation
happened), with a constant that can easily be comparable to the
cost of the mutation itself.
2)  Creating the event object includes various operations to grab the
information mutation event objects carry (e.g. the old and new
values for attribute changes); generating this can be expensive,
because generating the string representation of some attributes is
expensive (thing multi-dozen-kilobyte SVG path attribute, or large
inline style block), and because conversion from our internal types
to the ones mutation events want is expensive for nodes.

鉴于这段比较重要,所以我还是翻译一下吧

1.触发一个事件的时间复杂度是O(大小随着修改所在的dom深度变化),通过这个变量O,我们可以比较清楚的了解这个损耗的大小

2.创建一个事件对象的时候包括了各种操作,这些操作需要去抓取mutation事件对象时的信息,比如修改前的值和修改后的值(要做个比较)。执行这些操作是比较耗费资源的,因为为这些值生成一个字符串来做比较是很耗资源的(比如一堆SVG路径属性,或者大量的内联样式)。从一个内部类型转换到mutation事件对象所需要的类型也是非常耗时的。

那为什么不用oninput呢?

这是因为input事件只能监听textarea,type为password或者text的input,像type=checkbox这类的input就无法处理,显的不够通用。

理顺这些之后,就开始明白YUI和JQUERY各自选择的理由了

--完--
原文地址:http://www.f2es.com/better-way-listen-change/, 感谢原作者分享
posted @ 2014-09-30 11:08  沧幕  阅读(2132)  评论(0编辑  收藏  举报