jQuery源码阅读笔记(10)

事件处理后续

前文谈及了实现on()函数的实现,对当中的one参数和off()函数填坑

one参数用于区分one()on()one()函数只是多一个特点,事件函数只触发一次,触发后便移除

jquery.fn.extend({
  on(types, selector, data, fn) {
    return on(this, types, selector, data, fn);
  },
  one(types, selector, data, fn) {
    return on(this, types, selector, data, fn, 1);
  },
})

而涉及到移除,需要看另一个函数off(),移除事件函数

off([type[, fn]])
  • 概述:移除事件绑定

  • 解析:

    • 前面已经为事件移除做好了铺垫,只要根据参数做出不同反应就成
    • 没有参数时,是移除掉所有事件,不分类型
    • 只提供了类型,将此类型所有的事件移除
    • 只提供事件不行,并不会去翻找所有事件类型,将此事件删除
    • 提供类型和事件,定点清除,绑定了多次的需要全部清除
  • 实现:

    off(type, fn) {
      //没有参数时移除全部事件
      if (arguments.length === 0) {
        return this.each(function() {
          //遍历对象的事件存储属性
          $.each(this._eventsCache, (index, value) => {
            //index === 事件类型  value === 数组
            //遍历这个数组
            $.each(value, (_i, target) => {
              //target === 对象
              this.removeEventListener(index, target.callback);
            });
          });
          this._eventsCache = {};
        });
      } else if (arguments.length === 1) {
        //移除指定类型的事件
        return this.each(function() {
          $.each(this._eventsCache[type], (_i, target) => {
            this.removeEventListener(type, target.callback);
          });
          this._eventsCache[type] = [];
        });
      } else {
        //根据特定事件,移除
        return this.each(function() {
          //先找到相应的事件
          if (this._eventsCache && this._eventsCache[type]) {
            const arr = this._eventsCache[type];
            for (let i = 0; i < arr.length; i++) {
              const target = arr[i];
              if (target.fn === fn) {
                this.removeEventListener(type, target.callback);
                arr.splice(i, 1);
                i--;
              }
            }
          }
        });
      }
    }
    

    事实上还有一个参数,根据委托选择器删除,这一点并没有在此展开了

对于其他事件,只是on()函数的简化版

jquery.each(["blur", "change", "click", "dblclick", "error",
    "focus", "focusin", "focusout", "keydown", "keypress",
    "keyup", "mousedown", "mouseenter", "mouseleave",
    "mousemove", "mouseout", "mouseover", "mouseup",
    "resize", "scroll", "select", "submit", "unload"], function(_i, type) {
    jquery.fn[type] = function(data, fn) {
      //没有参数的时候,是触发
      if (arguments.length === 0) {
        return this.each(function() {
          $.each(this._eventsCache[type], (_i, target) => {
            target.callback.call(this);
          });
        });
      } else {
        if (arguments.length === 1) {
          fn = data;
          data = null;
        }
        return this.on(type, null, data, fn);
      }
    };

文档处理补充

事件移除实现之后,回头将前面遗漏的文档方法做一个补充

empty()
  • 概述:移除所有子节点

  • 实现:

    empty() {
      return this.each(function() {
        this.innerHTML = "";
      });
    }
    
detach([expr])
  • 概述:移除元素本身

  • 解析:

    • 可以接收一个参数,作为筛选表达式
  • 实现:

    detach(expr) {
      //没有参数或不合理参数时触发
      if (arguments.length === 0 || !isString(expr)) {
        return this.each(function() {
          this.remove();
        });
      }
      //先选择到表达式
      return this.each((_i, value) => {
        this.constructor(expr).each(function() {
          this === value && this.remove();
        });
      });
    }
    
remvoe([expr])
  • 概述:移除元素本身,并移除事件

  • 解析:

    • 简单看,就是一个组合动作,直接拿来用
  • 实现:

    remove(expr) {
      return this.off().detach(expr);
    }
    
clone([dataAndEvents[, deepDataAndEvents]])
  • 概述:克隆节点

  • 解析:

    • 两个可选参数,控制克隆表现,皆为布尔类型
    • 第一参数决定是否克隆节点事件
    • 当第一参数开启时,才关注第二参数
    • 第二参数默认为true,决定是否克隆子节点的事件
    • 返回值是一个克隆的新对象
  • 实现:

    clone(dataAndEvents = false, deepDataAndEvents = true) {
      const obj = this.constructor();
      this.each(function() {
        const node = this.cloneNode(true);
        if (dataAndEvents) {
          node._eventsCache = this._eventsCache;
          //遍历事件数据,再加一层
          $.each(this._eventsCache, function(key) {
            $.each(this, function() {
              node.addEventListener(key, this.callback);
            });
          });
          //克隆子元素的事件
          if (deepDataAndEvents && node.children.length) {
            $.each(node.children, (index, value) => {
              node.insertBefore($(this.children[index]).clone(true, true)[0], value);
              value.remove();
            });
          }
        }
        obj.push(node);
      });
      return obj;
    }
    
unwrap()
  • 概述:将自己的父元素移除

  • 解析:

    • 找到自己的父级元素,调用删除指令,不过不能把子元素移除掉了
    • 先将所有子元素移出来当作父级元素的兄弟
  • 实现:

    unwrap() {
      return this.each(function() {
        const target = this.parentNode;
        $(target).after(target.children).remove();
      });
    }
    
wrap(content)
  • 概述:在自己外围生成一圈元素包裹

  • 解析:

    • 与上者表现相反,添加
  • 实现:

    wrap(content) {
      const target = this.constructor(content).clone(true);
      return this.after(target).appendTo(target);
    }
    

    似乎还接收函数参数,将返回值作为外层包裹元素,此处并没有落实

wrapAll(content)
  • 概述:将所有匹配元素使用一个元素包裹起来

  • 解析:

    • 上者是每个匹配元素都生成一个,而此处是将所有匹配元素集合在一块,并使用一个元素包裹
  • 实现:

    wrapAll(content) {
      const target = this.constructor(content).clone(true);
      //将参数放置于第一个目标的附近
      return this.first().after(target).end().appendTo(target);
    }
    
wrapInner(content)
  • 概述:所有子元素用参数包裹起来

  • 解析:

    • 便是在自己的下围再圈一层
  • 实现:

    wrapInner(content) {
      const target = $(content).first();
      return this.each(function() {
        $(this).append(target.clone(true).append($(this.children)));
      });
    },
    
replaceWith(content)
  • 概述:自己作为地点,被参数替换

  • 解析:

    • 是一整个全部切换
  • 实现:

    replaceWith(content) {
      const target = $(content);
      const obj = this.before(target);
      this.remove();
      return obj;
    }
    
replaceAll(content)
  • 概述:将自身放置目标值位置,且将目标值移除

  • 解析:

    • 与前者不同之处,只是参数和匹配元素交换了一下
  • 实现:

    replaceAll(content) {
      const target = $(content);
      const obj = this.insertAfter(target);
      target.remove();
      return obj;
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值