Fe-interview: [js] 第113天 用js写一个事件侦听器的方法

Created on 6 Aug 2019  ·  8Comments  ·  Source: haizlin/fe-interview

第113天 用js写一个事件侦听器的方法

js

Most helpful comment

<body>
  <div>
    <span id="a">点击</span>
    <a href="#" id="b">超链接</a>
    <button id="d" onclick="remove()">点击移除事件绑定</button>
    <button onclick="add()">点击添加事件绑定</button>
  </div>
  <script>
    var eventTool = {
      //获取事件 window.event兼容ie
      getEvent: function (e) {
        return e || window.event
      },
      //获取事件触发元素 srcElement ie独有
      getTarget: function (e) {
        return e.target || e.srcElement
      },
      addListener: function (e, type, hander) {
        if (e.addEventListener) {
          //兼容ie ie没有事件捕获的说法 因此设为false
          //dom2 
          e.addEventListener(type, hander, false);
        } else if (e.attachEvent) {
          //IE
          e.attachEvent('on' + type, hander)
        } else {
          //dom0
          e['on' + type] = hander
        }
      },
      removeListener: function (e, type, hander) {
        if (e.removeEventListener) {
          e.removeEventListener(type, hander, false)
        } else if (e.detachEvent) {
          e.detachEvent('on' + type, hander)
        }
        else { e['on' + type] = null }
      },
      //阻止事件冒泡
      stopPropagation: function (e) {
        if (e.stopPropagation) {
          // ie以外阻止冒泡
          e.stopPropagation()
        }
        else {
          //ie独有阻止冒泡 新版本的chrome等浏览器也支持
          e.cancelBubble = true
        }
      },
      // 阻止事件相关元素的默认事件
      preventDefault: function (e) {
        if (e.preventDefault) {
          e.preventDefault()
        }
        else {
          //ie的阻止默认事件方式
          e.returnValue = false;
        }
      }
    }
    var btn = document.getElementById('a');
    var link = document.getElementById('b');
    var fun = function (e) {
      var e = eventTool.getEvent(e);
      var t = eventTool.getTarget(e);
      console.log(e)
      console.log(t)
      eventTool.stopPropagation(e)
    }
    var add = function () {
      eventTool.addListener(btn, 'click', fun)
      eventTool.addListener(link, 'click', fun)
    }
    var remove = function () {
      console.log(btn)
      eventTool.removeListener(btn, 'click', fun)
    }
  </script>
</body>

All 8 comments

题面讲得有点迷惑...我瞎猜一下它要我干啥吧

事件监听器:EventListener,就是 EventEmitter 触发事件时的 handler,是一个函数。

然后给这个函数增加一个方法?

这里有一份非常短的 Event Emitter 实现(甚至支持通配符:

class Ev {
    e = { '*': [] }
    on(n, h) { this.e[n] = (this.e[n] || []).concat(h) }
    emit(n, p) { this.e['*'].concat(this.e[n]).map(h => h(p)) }
}
题意不清..先写个。。
class EventBus {
  events = {};

  on(eventName, callback) {
    const events = this.events;
    if (!events[eventName]) {
      events[eventName] = [];
    }
    events[eventName].push(callback);
    return this;
  }

  emit(eventName, params) {
    const events = this.events;
    if (events[eventName]) {
      events[eventName].forEach(cb => cb(params));
    }
    return this;
  }
}
题意不清..先写个。。
class EventBus {
  events = {};

  on(eventName, callback) {
    const events = this.events;
    if (!events[eventName]) {
      events[eventName] = [];
    }
    events[eventName].push(callback);
    return this;
  }

  emit(eventName, params) {
    const events = this.events;
    if (events[eventName]) {
      events[eventName].forEach(cb => cb(params));
    }
    return this;
  }
}

events = {};这样直接赋值是不对的,需要用构造函数包裹。

 constructor() {
        this.events = {}
    }

@bertcai

events = {};这样直接赋值是不对的,需要用构造函数包裹。

 constructor() {
        this.events = {}
    }

类内直接声明成员是一个 Stage 3 草案;见此

题意不清..先写个。。
class EventBus {
  events = {};

  on(eventName, callback) {
    const events = this.events;
    if (!events[eventName]) {
      events[eventName] = [];
    }
    events[eventName].push(callback);
    return this;
  }

  emit(eventName, params) {
    const events = this.events;
    if (events[eventName]) {
      events[eventName].forEach(cb => cb(params));
    }
    return this;
  }
}

events = {};这样直接赋值是不对的,需要用构造函数包裹。

 constructor() {
        this.events = {}
    }

感谢指出..
实际可以这么写的,有babel..我本来写的ts。。删了private 之类的ㄟ( ▔, ▔ )ㄏ

<body>
  <div>
    <span id="a">点击</span>
    <a href="#" id="b">超链接</a>
    <button id="d" onclick="remove()">点击移除事件绑定</button>
    <button onclick="add()">点击添加事件绑定</button>
  </div>
  <script>
    var eventTool = {
      //获取事件 window.event兼容ie
      getEvent: function (e) {
        return e || window.event
      },
      //获取事件触发元素 srcElement ie独有
      getTarget: function (e) {
        return e.target || e.srcElement
      },
      addListener: function (e, type, hander) {
        if (e.addEventListener) {
          //兼容ie ie没有事件捕获的说法 因此设为false
          //dom2 
          e.addEventListener(type, hander, false);
        } else if (e.attachEvent) {
          //IE
          e.attachEvent('on' + type, hander)
        } else {
          //dom0
          e['on' + type] = hander
        }
      },
      removeListener: function (e, type, hander) {
        if (e.removeEventListener) {
          e.removeEventListener(type, hander, false)
        } else if (e.detachEvent) {
          e.detachEvent('on' + type, hander)
        }
        else { e['on' + type] = null }
      },
      //阻止事件冒泡
      stopPropagation: function (e) {
        if (e.stopPropagation) {
          // ie以外阻止冒泡
          e.stopPropagation()
        }
        else {
          //ie独有阻止冒泡 新版本的chrome等浏览器也支持
          e.cancelBubble = true
        }
      },
      // 阻止事件相关元素的默认事件
      preventDefault: function (e) {
        if (e.preventDefault) {
          e.preventDefault()
        }
        else {
          //ie的阻止默认事件方式
          e.returnValue = false;
        }
      }
    }
    var btn = document.getElementById('a');
    var link = document.getElementById('b');
    var fun = function (e) {
      var e = eventTool.getEvent(e);
      var t = eventTool.getTarget(e);
      console.log(e)
      console.log(t)
      eventTool.stopPropagation(e)
    }
    var add = function () {
      eventTool.addListener(btn, 'click', fun)
      eventTool.addListener(link, 'click', fun)
    }
    var remove = function () {
      console.log(btn)
      eventTool.removeListener(btn, 'click', fun)
    }
  </script>
</body>

function addEvent(elem, type, handle) {
if (elem.addEventListener) { //W3C标准
elem.addEventListener(type, handle, false); //事件冒泡时触发
} else if (elem.attachEvent) { //IE独有
elem.attachEvent('on' + type, function() {
handle.call(elem); //改变this使其指向指定元素
});
} else {
elem['on' + type] = handle; // 最古老
}
}

参考网上的代码加了点自己的东西

/**
 * 一个简单的发布订阅者模式
 */

interface IHandler {
  fn: Function,
  type: string,
  name: string
}

export default class EventUtils {
  /**
   * 键值对 对应事件名称以及数组的值
   */
  static handler = {}

  /**
   * on 方法 添加监听事件
   */
  static on (name: string, handler: Function): EventUtils {
    const i:IHandler = {
      fn: handler,
      type: 'on',
      name: name
    }
    if (Object.keys(EventUtils.handler).includes(name)) {
      EventUtils.handler[name].push(i)
      return EventUtils
    }
    EventUtils.handler[name] = [].concat(i)
    return EventUtils
  }

  /**
   * off 方法 移除监听事件
   */
  static off (name: string, handler: Function): EventUtils {
    const event: any[] = EventUtils.handler[name]
    if (event) {
      for (let i = event.length - 1; i >= 0; i--) {
        if (event[i].fn === handler) {
          event.splice(i, 1)
        }
      }
    }
    return EventUtils
  }

  /**
   * emit 方法 触发监听的事件
   */
  static emit (name: string, ...args: any): EventUtils {
    const event = EventUtils.handler[name]
    let newEvent = []
    event && event.length && event.forEach((item: IHandler, index: number) => {
      item.fn.call(this, ...args)

      // 如果有只监听一次的事件
      if (item.type !== 'once') {
        newEvent.push(event.slice(index, index + 1))
      }
    })

    const hasOnce = event && event.length && event.some((item: IHandler) => {
      return item.type === 'once'
    })

    if (hasOnce) {
      EventUtils.handler[name] = newEvent
    }

    // 这里做一个执行完成之后的 once代码 off 的操作
    return EventUtils
  }

  /**
   * once 方法 添加事件 只会被执行一次
   */
  static once (name: string, handler: Function): void {
    EventUtils.on(name, handler)
    EventUtils.handler[name][0]['type'] = 'once'
  }
}

Was this page helpful?
0 / 5 - 0 ratings