Heim > Web-Frontend > js-Tutorial > Hauptteil

So implementieren Sie einen benutzerdefinierten Ereignismechanismus mit Javascript

亚连
Freigeben: 2018-06-20 17:02:47
Original
1398 Leute haben es durchsucht

Mit der Entwicklung der Webtechnologie wird die Verwendung von JavaScript zum Anpassen von Objekten immer häufiger. Wenn Sie den von Ihnen erstellten Objekten Ereignismechanismen und externe Kommunikation über Ereignisse ermöglichen, kann die Entwicklungseffizienz erheblich verbessert werden. Der folgende Artikel führt Sie hauptsächlich in die relevanten Informationen zur Verwendung von Javascript zur Implementierung einer Reihe benutzerdefinierter Ereignismechanismen ein. Freunde in Not können darauf verweisen.

Vorwort

Der Ereignismechanismus bietet großen Komfort für unsere Webentwicklung und ermöglicht es uns, jederzeit anzugeben, welche Operation ausgeführt werden soll ausführen und welcher Code ausgeführt werden soll.

Klick-Ereignisse werden beispielsweise ausgelöst, wenn der Benutzer auf „Taste gedrückt“ und „Taste hoch“ klickt, und in der Upload-Steuerung werden Ereignisse ausgelöst, bevor die Datei hinzugefügt wird Der Upload ist abgeschlossen.

Da entsprechende Ereignisse zum richtigen Zeitpunkt ausgelöst werden, können wir entsprechende Verarbeitungsfunktionen für diese Ereignisse angeben und verschiedene personalisierte Vorgänge und Verarbeitungen in den ursprünglichen Prozess einfügen, wodurch der gesamte Prozess reicher wird.

Ereignisse wie Klick, Unschärfe, Fokus usw. sind native Ereignisse, die direkt vom ursprünglichen Dom bereitgestellt werden, während einige der verschiedenen Ereignisse, die von anderen von uns verwendeten Steuerelementen verwendet werden, im nativen Dom nicht verfügbar sind, z. B. in die Upload-Steuerung Normalerweise gibt es Start- und Abschlussereignisse für den Upload. Wie werden diese Ereignisse implementiert?

Ich möchte dem von mir entwickelten Steuerelement auch einen ähnlichen Ereignismechanismus hinzufügen. Finden wir es heraus.

Die Funktionen, die Ereignisse haben sollten

Vor der Implementierung analysieren wir zunächst die Grundfunktionen, die der Ereignismechanismus haben sollte.

Einfach ausgedrückt müssen Ereignisse die folgenden Funktionen bereitstellen:

  • Ereignis binden

  • Ereignis auslösen

  • Ereignis entbinden

Vorbereitung

Lassen Sie uns einen Blick darauf werfen Ein Merkmal eines Ereignisses, das Das Ereignis muss zu einem bestimmten Objekt gehören. Zum Beispiel: Fokus- und Unschärfeereignisse gelten für DOM-Elemente, die den Fokus erhalten können, Eingabeereignisse für Eingabefelder, Upload-Start und Upload-Erfolg für den Upload-Erfolg.

Mit anderen Worten, ein Ereignis existiert nicht unabhängig, es erfordert einen Träger. Wie können wir also dafür sorgen, dass Veranstaltungen einen Träger haben? Eine einfache Implementierungslösung besteht darin, Ereignisse als Basisklasse zu verwenden und diese Ereignisklasse überall dort zu erben, wo Ereignisse benötigt werden.

Wir benennen das Bindungsereignis, das Auslöseereignis und das Entbindungsereignis als: Ein, Auslösen bzw. Aus. Dann können wir einfach diese Ereignisklasse schreiben:

function CustomEvent() {
 this._events = {};
}
CustomEvent.prototype = {
 constructor: CustomEvent,
 // 绑定事件
 on: function () {
 },
 // 触发事件
 fire: function () {
 },
 // 取消绑定事件
 off: function () {
 }
};
Nach dem Login kopieren

Ereignisbindung

Implementieren Sie zunächst die Ereignisbindung. Bei der Ereignisbindung müssen der Ereignistyp und die Ereignisverarbeitungsfunktion angegeben werden.

Was brauchen Sie sonst noch? Wir sind ein benutzerdefiniertes Ereignis. Wir müssen nicht wie bei nativen Ereignissen angeben, ob es in der Bubbling-Phase oder der Capture-Phase ausgelöst wird. Wir müssen auch nicht angeben, welche Elemente zusätzlich ausgelöst werden sollen.

Dies ist in der Ereignisfunktion im Allgemeinen die aktuelle Instanz. Dies trifft in einigen Fällen möglicherweise nicht zu. Wir müssen den Kontext, in dem die Ereignisverarbeitungsfunktion ausgeführt wird, neu angeben.

Daher sind die drei Parameter bei der Bestimmung der Ereignisbindung: Ereignistyp, Ereignisverarbeitungsfunktion und Ausführungskontext der Ereignisverarbeitungsfunktion.

Was bewirkt die Ereignisbindung? Es ist eigentlich sehr einfach, den entsprechenden Ereignisnamen und die Ereignisverarbeitungsfunktion aufzuzeichnen.

Meine Implementierung ist wie folgt:

{
 /**
  * 绑定事件
  * 
  * @param {String} type 事件类型
  * @param {Function} fn 事件处理函数
  * @param {Object} scope 要为事件处理函数绑定的执行上下文
  * @returns 当前实例对象
  */
 on: function (type, fn, scope) {
  if (type + '' !== type) {
   console && console.error && console.error('the first argument type is requird as string');
   return this;
  }
  if (typeof fn != 'function') {
   console && console.error && console.error('the second argument fn is requird as function');
   return this;
  }
  type = type.toLowerCase();

  if (!this._events[type]) {
   this._events[type] = [];
  }
  this._events[type].push(scope ? [fn, scope] : [fn]);
  return this;
 }
}
Nach dem Login kopieren

Da ein Ereignis mehrmals gebunden und nacheinander ausgeführt werden kann, verwendet die Verarbeitungsfunktionsspeicherung unter allen Ereignistypen ein Array.

Ereignisauslösung

Die Grundfunktion der Ereignisauslösung besteht darin, das vom Benutzer gebundene Ereignis auszuführen, sodass nur überprüft werden muss, wann Das Ereignis wird ausgelöst. Wenn ja, rufen Sie es einfach auf.

Darüber hinaus handelt es sich bei der Ereignisauslösung tatsächlich um den Prozess der Ausführung der vom Benutzer angegebenen Verarbeitungsfunktion, und viele personalisierte Vorgänge werden auch in der vom Benutzer angegebenen Ereignisverarbeitungsfunktion ausgeführt, sodass die bloße Ausführung dieser Funktion nicht ausreicht . Für die aktuelle Funktion müssen auch notwendige Informationen bereitgestellt werden, wie z. B. das aktuell angeklickte Element im Click-Event, der Tastencode der aktuellen Taste im Keyboard-Event und die aktuellen Dateiinformationen im Upload-Start und Upload-Abschluss.

Wenn daher ein Ereignis ausgelöst wird, müssen die tatsächlichen Parameter der Ereignisverarbeitungsfunktion die grundlegenden Informationen des aktuellen Ereignisses enthalten.

Darüber hinaus müssen durch den Benutzervorgang in der Ereignisverarbeitungsfunktion möglicherweise die nachfolgenden Informationen angepasst werden. Beispielsweise kann der Benutzer im Keydwon-Ereignis die Eingabe dieses Schlüssels verbieten Wenn die Datei hochgeladen wurde, bricht der Benutzer das Hochladen oder Ändern einiger Dateiinformationen ab. Daher sollte die ereignisauslösende Funktion das vom Benutzer geänderte Ereignisobjekt zurückgeben.

Meine Implementierung ist wie folgt:

{
 /**
  * 触发事件
  * 
  * @param {String} type 触发事件的名称
  * @param {Object} data 要额外传递的数据,事件处理函数参数如下
  * event = {
   // 事件类型
   type: type,
   // 绑定的源,始终为当前实例对象
   origin: this,
   // 事件处理函数中的执行上下文 为 this 或用户指定的上下文对象
   scope :this/scope
   // 其他数据 为fire时传递的数据
  }
  * @returns 事件对象
  */
 fire: function (type, data) {
  type = type.toLowerCase();
  var eventArr = this._events[type];
  var fn,
   // event = {
   //  // 事件类型
   //  type: type,
   //  // 绑定的源
   //  origin: this,
   //  // scope 为 this 或用户指定的上下文,
   //  // 其他数据 
   //  data: data,
   //  // 是否取消
   //  cancel: false
   // };
   // 上面对自定义参数的处理不便于使用 将相关属性直接合并到事件参数中
   event = $.extend({
    // 事件类型
    type: type,
    // 绑定的源
    origin: this,
    // scope 为 this 或用户指定的上下文,
    // 其他数据 
    // data: data,
    // 是否取消
    cancel: false
   }, data);
  if (!eventArr) {
   return event;
  }
  for (var i = 0, l = eventArr.length; i < l; ++i) {
   fn = eventArr[i][0];
   event.scope = eventArr[i][1] || this;
   fn.call(event.scope, event);
  }
  return event;
 }
}
Nach dem Login kopieren

Die tatsächlichen Parameter, die der Ereignisverarbeitungsfunktion in der obigen Implementierung übergeben werden, müssen die folgenden Informationen enthalten:

  • Typ: der aktuell ausgelöste Ereignistyp

  • Ursprung: das Objekt, an das das aktuelle Ereignis gebunden ist

  • Umfang: das Ausführungskontext der Ereignisverarbeitungsfunktion

Darüber hinaus können verschiedene Ereignisse bei Auslösung unterschiedliche Informationen zu diesem Ereignisobjekt hinzufügen.

关于 Object.assign(target, ...sources) 是ES6中的一个方法,作用是将所有可枚举属性的值从一个或多个源对象复制到目标对象,并返回目标对象,类似于大家熟知的$.extend(target,..sources) 方法。

事件取消

事件取消中需要做的就是已经绑定的事件处理函数移除掉即可。

实现如下:

{
 /**
  * 取消绑定一个事件
  * 
  * @param {String} type 取消绑定的事件名称
  * @param {Function} fn 要取消绑定的事件处理函数,不指定则移除当前事件类型下的全部处理函数
  * @returns 当前实例对象
  */
 off: function (type, fn) {
  type = type.toLowerCase();
  var eventArr = this._events[type];
  if (!eventArr || !eventArr.length) return this;
  if (!fn) {
   this._events[type] = eventArr = [];
  } else {
   for (var i = 0; i < eventArr.length; ++i) {
    if (fn === eventArr[i][0]) {
     eventArr.splice(i, 1);
     // 1、找到后不能立即 break 可能存在一个事件一个函数绑定多次的情况
     // 删除后数组改变,下一个仍然需要遍历处理!
     --i;
    }
   }
  }
  return this;
 }
}
Nach dem Login kopieren

此处实现类似原生的事件取消绑定,如果指定了事件处理函数则移除指定事件的指定处理函数,如果省略事件处理函数则移除当前事件类型下的所有事件处理函数。

仅触发一次的事件

jQuery中有一个 one 方法,它所绑定的事件仅会执行一次,此方法在一些特定情况下非常有用,不需要用户手动取消绑定这个事件。

这里的实现也非常简单,只用在触发这个事件时取消绑定即可。

实现如下:

{
 /**
  * 绑定一个只执行一次的事件
  * 
  * @param {String} type 事件类型
  * @param {Function} fn 事件处理函数
  * @param {Object} scope 要为事件处理函数绑定的执行上下文
  * @returns 当前实例对象
  */
 one: function (type, fn, scope) {
  var that = this;
  function nfn() {
   // 执行时 先取消绑定
   that.off(type, nfn);
   // 再执行函数
   fn.apply(scope || that, arguments);
  }
  this.on(type, nfn, scope);
  return this;
 }
}
Nach dem Login kopieren

原理则是不把用户指定的函数直接绑定上去,而是生成一个新的函数,并绑定,此函数执行时会先取消绑定,再执行用户指定的处理函数。

基本雏形

到此,一套完整的事件机制就已经完成了,完整代码如下:

function CustomEvent() {
 this._events = {};
}
CustomEvent.prototype = {
 constructor: CustomEvent,
 /**
  * 绑定事件
  * 
  * @param {String} type 事件类型
  * @param {Function} fn 事件处理函数
  * @param {Object} scope 要为事件处理函数绑定的执行上下文
  * @returns 当前实例对象
  */
 on: function (type, fn, scope) {
  if (type + &#39;&#39; !== type) {
   console && console.error && console.error(&#39;the first argument type is requird as string&#39;);
   return this;
  }
  if (typeof fn != &#39;function&#39;) {
   console && console.error && console.error(&#39;the second argument fn is requird as function&#39;);
   return this;
  }
  type = type.toLowerCase();

  if (!this._events[type]) {
   this._events[type] = [];
  }
  this._events[type].push(scope ? [fn, scope] : [fn]);

  return this;
 },
 /**
  * 触发事件
  * 
  * @param {String} type 触发事件的名称
  * @param {Anything} data 要额外传递的数据,事件处理函数参数如下
  * event = {
   // 事件类型
   type: type,
   // 绑定的源,始终为当前实例对象
   origin: this,
   // 事件处理函数中的执行上下文 为 this 或用户指定的上下文对象
   scope :this/scope
   // 其他数据 为fire时传递的数据
  }
  * @returns 事件对象
  */
 fire: function (type, data) {
  type = type.toLowerCase();
  var eventArr = this._events[type];
  var fn, scope,
   event = Object.assign({
    // 事件类型
    type: type,
    // 绑定的源
    origin: this,
    // scope 为 this 或用户指定的上下文,
    // 是否取消
    cancel: false
   }, data);

  if (!eventArr) return event;

  for (var i = 0, l = eventArr.length; i < l; ++i) {
   fn = eventArr[i][0];
   scope = eventArr[i][1];
   if (scope) {
    event.scope = scope;
    fn.call(scope, event);
   } else {
    event.scope = this;
    fn(event);
   }
  }
  return event;
 },
 /**
  * 取消绑定一个事件
  * 
  * @param {String} type 取消绑定的事件名称
  * @param {Function} fn 要取消绑定的事件处理函数,不指定则移除当前事件类型下的全部处理函数
  * @returns 当前实例对象
  */
 off: function (type, fn) {
  type = type.toLowerCase();
  var eventArr = this._events[type];
  if (!eventArr || !eventArr.length) return this;
  if (!fn) {
   this._events[type] = eventArr = [];
  } else {
   for (var i = 0; i < eventArr.length; ++i) {
    if (fn === eventArr[i][0]) {
     eventArr.splice(i, 1);
     // 1、找到后不能立即 break 可能存在一个事件一个函数绑定多次的情况
     // 删除后数组改变,下一个仍然需要遍历处理!
     --i;
    }
   }
  }
  return this;
 },
 /**
  * 绑定一个只执行一次的事件
  * 
  * @param {String} type 事件类型
  * @param {Function} fn 事件处理函数
  * @param {Object} scope 要为事件处理函数绑定的执行上下文
  * @returns 当前实例对象
  */
 one: function (type, fn, scope) {
  var that = this;

  function nfn() {
   // 执行时 先取消绑定
   that.off(type, nfn);
   // 再执行函数
   fn.apply(scope || that, arguments);
  }
  this.on(type, nfn, scope);
  return this;
 }
};
Nach dem Login kopieren

在自己的控件中使用

上面已经实现了一套事件机制,我们如何在自己的事件中使用呢。

比如我写了一个日历控件,需要使用事件机制。

function Calendar() {
 // 加入事件机制的存储的对象
 this._event = {};
 // 日历的其他实现
}
Calendar.prototype = {
 constructor:Calendar,
 on:function () {},
 off:function () {},
 fire:function () {},
 one:function () {},
 // 日历的其他实现 。。。
}
Nach dem Login kopieren

以上伪代码作为示意,仅需在让控件继承到on、off 、fire 、one等方法即可。但是必须保证事件的存储对象_events 必须是直接加载实例上的,这点需要在继承时注意,JavaScript中实现继承的方案太多了。

上面为日历控件Calendar中加入了事件机制,之后就可以在Calendar中使用了。

如在日历开发时,我们在日历的单元格渲染时触发cellRender事件。

// 每天渲染时发生 还未插入页面
var renderEvent = this.fire(&#39;cellRender&#39;, {
 // 当天的完整日期
 date: date.format(&#39;YYYY-MM-DD&#39;),
 // 当天的iso星期
 isoWeekday: day,
 // 日历dom
 el: this.el,
 // 当前单元格
 tdEl: td,
 // 日期文本
 dateText: text.innerText,
 // 日期class
 dateCls: text.className,
 // 需要注入的额外的html
 extraHtml: &#39;&#39;,
 isHeader: false
});
Nach dem Login kopieren

在事件中,我们将当前渲染的日期、文本class等信息都提供给用户,这样用户就可以绑定这个事件,在这个事件中进行自己的个性阿化处理了。

如在渲染时,如果是周末则插入一个"假"的标识,并让日期显示为红色。

var calendar = new Calendar();
calendar.on(&#39;cellRender&#39;, function (e) {
 if(e.isoWeekday > 5 ) {
  e.extraHtml = &#39;<span>假</span>&#39;;
  e.dateCls += &#39; red&#39;;
 } 
});
Nach dem Login kopieren

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

使用jQuery封装animate.css(详细教程)

vue-cli配置文件(详细教程)

使用Vue2.x如何实现JSON树

在Angular4中有关CLI的安装与使用教程

使用JS实现换图时钟(详细教程)

Das obige ist der detaillierte Inhalt vonSo implementieren Sie einen benutzerdefinierten Ereignismechanismus mit Javascript. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Quelle:php.cn
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage
Über uns Haftungsausschluss Sitemap
Chinesische PHP-Website:Online-PHP-Schulung für das Gemeinwohl,Helfen Sie PHP-Lernenden, sich schnell weiterzuentwickeln!