웹 프론트엔드 JS 튜토리얼 jQuery 1.9.1 소스코드 분석 시리즈 (10) system_jquery 이벤트 바인딩 이벤트

jQuery 1.9.1 소스코드 분석 시리즈 (10) system_jquery 이벤트 바인딩 이벤트

May 16, 2016 pm 03:31 PM

事件绑定的方法有很多种,使用了jquery那么原理那种绑定方式(elem.click = function(){...}))就不太想推荐给大家了。最主要的原因是elem.click=fn这种方式只能绑定一个事件处理,多次绑定的只会保留最后一次绑定的结果。

下面给大家介绍jquery绑定事件的方式有哪些吧。

复制代码 代码如下:

jQuery.fn.eventType([[data,] fn])

比如eventType指的是事件类型,比如click: $("#chua").click(fn);

data这个参数一般都不会使用。这种方式事件绑定在("#chua")上,没有委托事件,和js原生的事件绑定更接近。我们看一下源码

1

2

3

4

5

6

7

8

9

10

11

12

jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +

  "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +

  "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) {

      //合并15种事件统一增加到jQuery.fn上,内部调用this.on / this.trigger

      jQuery.fn[ name ] = function( data, fn ) {

      return arguments.length > 0 ?

      this.on( name, null, data, fn ) :

      //如果不带参数表示立刻触发指定事件

      this.trigger( name );

   };

});

jQuery.fn.bind( types[, data], fn )

로그인 후 복사

  比如$("#chua").bind("click",fn)。直接将事件绑定到$("#chua")上,没有委托事件。源码

1

2

3

4

5

6

7

bind: function( types, data, fn ) {

 return this.on( types, null, data, fn );

},

unbind: function( types, fn ) {

 return this.off( types, null, fn );

}

 jQuery.fn.delegate(selector, types[, data], fn)

로그인 후 복사

  顾名思义delegate这个函数是用来做事件委托的,将选择器selector对应的响应处理委托给当前jQuery所匹配的元素。

  比如:$(document).delegate('#big',"click",dohander);分析到这里顺便分析一下事件委托的处理流程。

  当点击"#big"元素的时候,事件click会冒泡直到document节点;

  document绑定了处理事件,这个处理事件会调用到事件分发器dispatch;

  dispatch中取出对应事件类型click的所有的委托事件列表handlers;

  根据事件源event.target过滤出委托事件列表handlers中每一个元素的selector属性对应的节点处于事件原和委托节点document之间(包括事件源)的委托事件,保存为handlerQueue;

  执行handlerQueue里面的事件处理。

  上面是一个大致的流程,后续会详细分析。先看delegate源码

1

2

3

4

5

6

7

8

delegate: function( selector, types, data, fn ) {

 return this.on( types, selector, data, fn );

},

undelegate: function( selector, types, fn ) {

 // ( namespace ) or ( selector, types [, fn] )

 return arguments.length === 1 ? this.off( selector, "**" ) : this.off( types, selector || "**", fn );

}

jQuery.fn.one( types[, selector[, data]], fn )

로그인 후 복사

  通过one()函数绑定的事件处理函数都是一次性的,只有首次触发事件时会执行该事件处理函数。触发之后,jQuery就会移除当前事件绑定。

  比如$("#chua").one("click",fn);为#chua节点绑定一次性的click事件

  $(document).one("click","#chua",fn);将#chua的click事件委托给document处理。源码

1

2

3

4

5

one: function( types, selector, data, fn ) {

  return this.on( types, selector, data, fn, 1 );

}

jQuery.fn.trigger(type[, data])

jQuery.fn.triggerHandler(type[, data])

로그인 후 복사

  trigger触发jQuery对象所匹配的每一个元素对应type类型的事件。比如$("#chua").trigger("click");

  triggeHandler只触发jQuery对象所匹配的元素中的第一个元素对应的type类型的事件,且不会触发事件的默认行为。

1

2

3

4

5

6

7

8

9

10

11

12

13

//立刻触发jQuery对象内所有元素的指定type的事件

trigger: function( type, data ) {

 return this.each(function() {

  jQuery.event.trigger( type, data, this );

 });

},

//立刻触发jQuery对象内第一个元素的指定type的事件,且不会触发事件(比如表单提交)的默认行为

triggerHandler: function( type, data ) {

 var elem = this[0];

 if ( elem ) {

  return jQuery.event.trigger( type, data, elem, true );

 }

}

로그인 후 복사

  上面分析了那么些个事件绑定,有么有发现他们都是使用.on方式绑定的?这也是为什么提倡统一使用on来绑定的原因(one方式除外)。

1

jQuery.fn.on( types[, selector[, data]], fn )

로그인 후 복사

  .on的事件绑定一半的代码都实在处理传递不同参数的处理,这也是jQuery的口号Write less, do more的代价吧。最终使用jQuery.event.add来绑定事件。

  jQuery.event.add绑定事件有几个比较关键的地方:

  第一个,使用内部缓存来保存节点elem的事件信息

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

//获取缓存数据

       elemData = jQuery._data( elem );

       ...

       

       //设置缓存数据

   if ( !(events = elemData.events) ) {

    events = elemData.events = {};

   }

   if ( !(eventHandle = elemData.handle) ) {

    eventHandle = elemData.handle = function( e ) {

     ...

    };

    //将elem作为handle函数的一个特征防止ie非本地事件引起的内存泄露

    eventHandle.elem = elem;

   }

로그인 후 복사

  第二个,设置绑定事件信息,特别是指定的选择器selector、响应处理handler、响应事件类型type、命名空间namespace

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

// handleObj:设置绑定事件信息。贯穿整个事件处理

 handleObj = jQuery.extend({

  type: type,

  origType: origType,

  data: data,

  handler: handler,

  guid: handler.guid,

  selector: selector,

  // For use in libraries implementing .is(). We use this for POS matching in `select`

  //"needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" +

  //whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )

  //用来判断亲密关系

  needsContext: selector && jQuery.expr.match.needsContext.test( selector ),

  namespace: namespaces.join(".")

 }, handleObjIn );

로그인 후 복사

  第三个,节点的事件列表中,真正的委托事件列表放置在前面,和delegateCount属性同步,即events.click.length假设为3,events.click.delegateCount假设为2。那么events.click[0]和events.click[1]所指定事件是委托事件。第三个events.click[2]对应的事件不是委托事件,而是节点自身的事件。

1

2

3

4

5

6

//将事件对象handleObj添加到元素的处理列表,委托事件放在前面,委托代理计数递增

 if ( selector ) {

  handlers.splice( handlers.delegateCount++, 0, handleObj );

 } else {

  handlers.push( handleObj );

 }

로그인 후 복사

  源码和添加事件后的结构上一章已经分析,详情请点击查看

  绑定有一个公用函数jQuery.fn.on。解绑同样有一个公用函数jQuery.fn.off

1

jQuery.fn.off([ types[, selector][, fn]] )

로그인 후 복사

  这里的传参有个比较特殊的情况:当types是浏览器事件对象event的时候,表示要去掉(解绑)委托节点上event.selector指定的委托事件

1

2

3

4

5

6

7

8

9

10

11

12

13

//传入的参数是事件且绑定了处理函数

if ( types && types.preventDefault && types.handleObj ) {

  // ( event ) dispatched jQuery.Event

  handleObj = types.handleObj;

  //types.delegateTarget是事件托管对象

  jQuery( types.delegateTarget ).off(

   //组合jQuery识别的type

   handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType,

   handleObj.selector,

   handleObj.handler

   );

  return this;

}

로그인 후 복사

  无论如何最终都是调用jQuery.event.remove函数来解绑事件。

  jQuery.fn.off完整的源码如下

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

off: function( types, selector, fn ) {

  var handleObj, type;

  //传入的参数是事件且绑定了处理函数

  if ( types && types.preventDefault && types.handleObj ) {

  // ( event ) dispatched jQuery.Event

  handleObj = types.handleObj;

  //types.delegateTarget是事件托管对象

  jQuery( types.delegateTarget ).off(

   //组合jQuery识别的type

   handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType,

   handleObj.selector,

   handleObj.handler

   );

  return this;

  }

  if ( typeof types === "object" ) {

  // ( types-object [, selector] )

  for ( type in types ) {

   this.off( type, selector, types[ type ] );

  }

  return this;

  }

  if ( selector === false || typeof selector === "function" ) {

  // ( types [, fn] )

  fn = selector;

  selector = undefined;

  }

  if ( fn === false ) {

  fn = returnFalse;

  }

  return this.each(function() {

  jQuery.event.remove( this, types, fn, selector );

  });

}

로그인 후 복사

  接下来分析一下事件解绑的低级api jQuery.event.remove。

jQuery.event.remove

  jQuery使用.off()函数伤处绑定的事件时内部调用的基础函数是jQuery.event.remove。该函数的处理流程如下

  1. 分解传入的要删除的事件类型types,遍历类型,如果要删除的事件没有事件名,只有命名空间则表示删除该命名空间下所有绑定事件

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

//分解types为type.namespace为单位元素的数组

types = ( types || "" ).match( core_rnotwhite ) || [""];

t = types.length;

while ( t-- ) {

   tmp = rtypenamespace.exec( types[t] ) || [];

   type = origType = tmp[1];

   namespaces = ( tmp[2] || "" ).split( "." ).sort();

 

   //解绑当前元素的当前命名空间(types[ t ])上所有的事件

   if ( !type ) {

    for ( type in events ) {

     jQuery.event.remove( elem, type + types[ t ], handler, selector, true );

    }

    continue;

   }

   ...

로그인 후 복사

  2. 遍历类型过程中,删除匹配的事件,代理计数修正

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

type = ( selector ? special.delegateType : special.bindType ) || type;

handlers = events[ type ] || [];

tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" );

//删除匹配事件

origCount = j = handlers.length;

while ( j-- ) {

   handleObj = handlers[ j ];

 

   //各种满足移除事件的条件才能移除

   if ( ( mappedTypes || origType === handleObj.origType ) &&

    ( !handler || handler.guid === handleObj.guid ) &&

    ( !tmp || tmp.test( handleObj.namespace ) ) &&

    ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) {

    handlers.splice( j, 1 );

        if ( handleObj.selector ) {

          handlers.delegateCount--;

        }

        if ( special.remove ) {

          special.remove.call( elem, handleObj );

        }

   }

}

로그인 후 복사

  3. 如果节点上指定类型的事件处理器已经为空,则将events上的该类型的事件处理对象移除

1

2

3

4

5

6

7

8

9

10

11

// 移除事件处理对象

// (移除特殊事件处理过程中避免潜在的无限递归,下一章会专门详解这种情况)

if ( origCount && !handlers.length ) {

//例如 var js_obj = document.createElement("div"); js_obj.onclick = function(){ …}

//上面的js_obj是一个DOM元素的引用,DOM元素它长期在网页当中,不会消失,而这个DOM元素的一属性onclick,又是内部的函数引用(闭包),而这个匿名函数又和js_obj之间有隐藏的关联(作用域链)所以形成了一个,循环引用.

if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) {

  jQuery.removeEvent( elem, type, elemData.handle );

}

 

delete events[ type ];

}

로그인 후 복사

  4. 如果节点上没有任何绑定的事件,则清空事件处理入口handle

1

2

3

4

5

if ( jQuery.isEmptyObject( events ) ) {

  delete elemData.handle;

  //removeData还检事件对象是否为空,所以使用它替代delete

  jQuery._removeData( elem, "events" );

}

로그인 후 복사

拓展: 浏览器事件删除jQuery.removeEvent

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

jQuery.removeEvent = document.removeEventListener ?

function( elem, type, handle ) {

   if ( elem.removeEventListener ) {

    elem.removeEventListener( type, handle, false );

   }

} :

function( elem, type, handle ) {

   var name = "on" + type;

   if ( elem.detachEvent ) {

    // #8545, #7054,避免自定义事件在IE6-8中的内存泄露

    // detachEvent需要传递第一个参数,不能是undefined的

    if ( typeof elem[ name ] === core_strundefined ) {

     elem[ name ] = null;

    }

    elem.detachEvent( name, handle );

   }

};

로그인 후 복사

以上内容是小编给大家介绍的jQuery 1.9.1源码分析系列(十)事件系统之绑定事件,希望大家喜欢。

본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

Video Face Swap

Video Face Swap

완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

뜨거운 도구

메모장++7.3.1

메모장++7.3.1

사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전

SublimeText3 중국어 버전

중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경

드림위버 CS6

드림위버 CS6

시각적 웹 개발 도구

SublimeText3 Mac 버전

SublimeText3 Mac 버전

신 수준의 코드 편집 소프트웨어(SublimeText3)

프론트 엔드 열 용지 영수증에 대한 차량 코드 인쇄를 만나면 어떻게해야합니까? 프론트 엔드 열 용지 영수증에 대한 차량 코드 인쇄를 만나면 어떻게해야합니까? Apr 04, 2025 pm 02:42 PM

프론트 엔드 개발시 프론트 엔드 열지대 티켓 인쇄를위한 자주 묻는 질문과 솔루션, 티켓 인쇄는 일반적인 요구 사항입니다. 그러나 많은 개발자들이 구현하고 있습니다 ...

Demystifying JavaScript : 그것이하는 일과 중요한 이유 Demystifying JavaScript : 그것이하는 일과 중요한 이유 Apr 09, 2025 am 12:07 AM

JavaScript는 현대 웹 개발의 초석이며 주요 기능에는 이벤트 중심 프로그래밍, 동적 컨텐츠 생성 및 비동기 프로그래밍이 포함됩니다. 1) 이벤트 중심 프로그래밍을 사용하면 사용자 작업에 따라 웹 페이지가 동적으로 변경 될 수 있습니다. 2) 동적 컨텐츠 생성을 사용하면 조건에 따라 페이지 컨텐츠를 조정할 수 있습니다. 3) 비동기 프로그래밍은 사용자 인터페이스가 차단되지 않도록합니다. JavaScript는 웹 상호 작용, 단일 페이지 응용 프로그램 및 서버 측 개발에 널리 사용되며 사용자 경험 및 크로스 플랫폼 개발의 유연성을 크게 향상시킵니다.

누가 더 많은 파이썬이나 자바 스크립트를 지불합니까? 누가 더 많은 파이썬이나 자바 스크립트를 지불합니까? Apr 04, 2025 am 12:09 AM

기술 및 산업 요구에 따라 Python 및 JavaScript 개발자에 대한 절대 급여는 없습니다. 1. 파이썬은 데이터 과학 및 기계 학습에서 더 많은 비용을 지불 할 수 있습니다. 2. JavaScript는 프론트 엔드 및 풀 스택 개발에 큰 수요가 있으며 급여도 상당합니다. 3. 영향 요인에는 경험, 지리적 위치, 회사 규모 및 특정 기술이 포함됩니다.

JavaScript를 사용하여 동일한 ID와 동일한 ID로 배열 요소를 하나의 객체로 병합하는 방법은 무엇입니까? JavaScript를 사용하여 동일한 ID와 동일한 ID로 배열 요소를 하나의 객체로 병합하는 방법은 무엇입니까? Apr 04, 2025 pm 05:09 PM

동일한 ID로 배열 요소를 JavaScript의 하나의 객체로 병합하는 방법은 무엇입니까? 데이터를 처리 할 때 종종 동일한 ID를 가질 필요가 있습니다 ...

JavaScript는 배우기가 어렵습니까? JavaScript는 배우기가 어렵습니까? Apr 03, 2025 am 12:20 AM

JavaScript를 배우는 것은 어렵지 않지만 어려운 일입니다. 1) 변수, 데이터 유형, 기능 등과 같은 기본 개념을 이해합니다. 2) 마스터 비동기 프로그래밍 및 이벤트 루프를 통해이를 구현하십시오. 3) DOM 운영을 사용하고 비동기 요청을 처리합니다. 4) 일반적인 실수를 피하고 디버깅 기술을 사용하십시오. 5) 성능을 최적화하고 모범 사례를 따르십시오.

Shiseido의 공식 웹 사이트와 같은 시차 스크롤 및 요소 애니메이션 효과를 달성하는 방법은 무엇입니까?
또는:
Shiseido의 공식 웹 사이트와 같은 페이지 스크롤과 함께 애니메이션 효과를 어떻게 달성 할 수 있습니까? Shiseido의 공식 웹 사이트와 같은 시차 스크롤 및 요소 애니메이션 효과를 달성하는 방법은 무엇입니까? 또는: Shiseido의 공식 웹 사이트와 같은 페이지 스크롤과 함께 애니메이션 효과를 어떻게 달성 할 수 있습니까? Apr 04, 2025 pm 05:36 PM

이 기사에서 시차 스크롤 및 요소 애니메이션 효과 실현에 대한 토론은 Shiseido 공식 웹 사이트 (https://www.shiseido.co.jp/sb/wonderland/)와 유사하게 달성하는 방법을 살펴볼 것입니다.

Console.log 출력 결과의 차이 : 두 통화가 다른 이유는 무엇입니까? Console.log 출력 결과의 차이 : 두 통화가 다른 이유는 무엇입니까? Apr 04, 2025 pm 05:12 PM

Console.log 출력의 차이의 근본 원인에 대한 심층적 인 논의. 이 기사에서는 Console.log 함수의 출력 결과의 차이점을 코드에서 분석하고 그에 따른 이유를 설명합니다. � ...

JavaScript의 진화 : 현재 동향과 미래 전망 JavaScript의 진화 : 현재 동향과 미래 전망 Apr 10, 2025 am 09:33 AM

JavaScript의 최신 트렌드에는 Typescript의 Rise, 현대 프레임 워크 및 라이브러리의 인기 및 WebAssembly의 적용이 포함됩니다. 향후 전망은보다 강력한 유형 시스템, 서버 측 JavaScript 개발, 인공 지능 및 기계 학습의 확장, IoT 및 Edge 컴퓨팅의 잠재력을 포함합니다.

See all articles