【问题标题】:Implementing events in custom object在自定义对象中实现事件
【发布时间】:2012-06-11 10:22:37
【问题描述】:

我想要的是一个提供一些事件的自定义对象。 例如:

var CustomObjectTextChangedEventName = 'textChanged';
var CustomObject = function () {
    var _this = this;
    var _text = "";

    _this.OnTextChanged = document.createEvent("Event");
    _this.OnTextChanged.initEvent(CustomObjectTextChangedEventName, true, false);

    _this.ChangeText = function (newText) {
        _text = newText;
        fireTextChanged();
    };

    function fireTextChanged() {
        _this.dispatchEvent(_this.OnTextChanged);
    }
}

使用该事件的代码如下所示:

myCustomObject = new CustomObject();
myCustomObject.addEventListener(CustomObjectTextChangedEventName, handleTextChanged, false);

如您所见...在JS中使用事件的默认方式,但我无法使其工作...

目前我的问题是我的对象没有实现addEventListenerdispatchEvent,但是这个函数通常是从“元素”实现的...

我可以以某种方式使它们可用,还是我必须自己实现它们? 我必须如何实现它们?

我必须实现自己的事件处理吗? (有一个处理程序的内部列表,一个“添加”和“删除”处理程序函数,并在我想触发事件时触发每个处理程序)?

【问题讨论】:

  • 我不知道这是否有帮助,但 Backbone.js 内置了事件。使用它创建的每个模型都可以使用语法object.trigger(event_name) 触发自定义事件
  • 使用 jQuery 也很容易......这就是为什么我认为它不会那么难:P $(_this).trigger(CustomObjectTextChangedEventName, _text); $(myCustomObject).bind(CustomObjectTextChangedEventName, handleTextChanged);

标签: javascript dom-events


【解决方案1】:

addEventListener 函数是Element 类的方法。一种方法是让CustomObject 继承自Element,如下所示:

CustomObject.prototype = Element.prototype;

问题是Element 类在不同的浏览器中可能有不同的实现。因此,例如触发事件可能并不容易(请参阅this post)。

所以我建议你自己做。不难,试试这样:

var CustomObject = function () {
    var _this = this;
    _this.events = {};

    _this.addEventListener = function(name, handler) {
        if (_this.events.hasOwnProperty(name))
            _this.events[name].push(handler);
        else
            _this.events[name] = [handler];
    };

    _this.removeEventListener = function(name, handler) {
        /* This is a bit tricky, because how would you identify functions?
           This simple solution should work if you pass THE SAME handler. */
        if (!_this.events.hasOwnProperty(name))
            return;

        var index = _this.events[name].indexOf(handler);
        if (index != -1)
            _this.events[name].splice(index, 1);
    };

    _this.fireEvent = function(name, args) {
        if (!_this.events.hasOwnProperty(name))
            return;

        if (!args || !args.length)
            args = [];

        var evs = _this.events[name], l = evs.length;
        for (var i = 0; i < l; i++) {
            evs[i].apply(null, args);
        }
    };
}

现在使用它很简单:

var co = new CustomObject();
co.addEventListener('textChange', function(name) {
    console.log(name); 
});
co.fireEvent('textChange', ['test']);

这是一个基本的解决方案。你可能想改变它,但我认为你应该掌握这个想法。

【讨论】:

  • 这就是我所说的“有一个内部处理程序列表、一个“添加”和“删除”处理程序函数,并在我想触发事件时触发每个处理程序”=)谢谢!
  • @DominikKirschenhofer 酷。我忘记了remove 处理程序,所以我刚刚添加了它。它应该工作。 :)
  • 关于“棘手”部分 - 通过传递相同的函数对象来删除处理程序:本机事件处理程序也是如此。我刚刚测试了一个按钮click 事件,removeEventListener 不起作用,除非我提供了对同一个函数对象的引用。
  • 异想天开,你为什么一开始用var _this = this
  • 哦,nvm,因为你想在子函数中使用它,this 会被重新声明。
【解决方案2】:

我不确定是否 100%,但接下来是我对这个问题的旧研究的结果:

  • 您无法以某种方式提供此功能。
  • 您可以简单地实现自己的逻辑。为此,您可以使用 MDN element.removeEventListener 文章中存在的代码,只需稍作更改。在它下面复制\过去的代码来自 MDN 链接:

// code source: MDN: https://developer.mozilla.org/en/DOM/element.removeEventListener
// without changes
if (!Element.prototype.addEventListener) {  
  var oListeners = {};  
  function runListeners(oEvent) {  
    if (!oEvent) { oEvent = window.event; }  
    for (var iLstId = 0, iElId = 0, oEvtListeners = oListeners[oEvent.type]; iElId < oEvtListeners.aEls.length; iElId++) {  
      if (oEvtListeners.aEls[iElId] === this) {  
        for (iLstId; iLstId < oEvtListeners.aEvts[iElId].length; iLstId++) { oEvtListeners.aEvts[iElId][iLstId].call(this, oEvent); }  
        break;  
      }  
    }  
  }  
  Element.prototype.addEventListener = function (sEventType, fListener /*, useCapture (will be ignored!) */) {  
    if (oListeners.hasOwnProperty(sEventType)) {  
      var oEvtListeners = oListeners[sEventType];  
      for (var nElIdx = -1, iElId = 0; iElId < oEvtListeners.aEls.length; iElId++) {  
        if (oEvtListeners.aEls[iElId] === this) { nElIdx = iElId; break; }  
      }  
      if (nElIdx === -1) {  
        oEvtListeners.aEls.push(this);  
        oEvtListeners.aEvts.push([fListener]);  
        this["on" + sEventType] = runListeners;  
      } else {  
        var aElListeners = oEvtListeners.aEvts[nElIdx];  
        if (this["on" + sEventType] !== runListeners) {  
          aElListeners.splice(0);  
          this["on" + sEventType] = runListeners;  
        }  
        for (var iLstId = 0; iLstId < aElListeners.length; iLstId++) {  
          if (aElListeners[iLstId] === fListener) { return; }  
        }       
        aElListeners.push(fListener);  
      }  
    } else {  
      oListeners[sEventType] = { aEls: [this], aEvts: [ [fListener] ] };  
      this["on" + sEventType] = runListeners;  
    }  
  };  
  Element.prototype.removeEventListener = function (sEventType, fListener /*, useCapture (will be ignored!) */) {  
    if (!oListeners.hasOwnProperty(sEventType)) { return; }  
    var oEvtListeners = oListeners[sEventType];  
    for (var nElIdx = -1, iElId = 0; iElId < oEvtListeners.aEls.length; iElId++) {  
      if (oEvtListeners.aEls[iElId] === this) { nElIdx = iElId; break; }  
    }  
    if (nElIdx === -1) { return; }  
    for (var iLstId = 0, aElListeners = oEvtListeners.aEvts[nElIdx]; iLstId < aElListeners.length; iLstId++) {  
      if (aElListeners[iLstId] === fListener) { aElListeners.splice(iLstId, 1); }  
    }  
  };  
}  
  • 我认为您需要更改的只是将Element.prototype 替换为CustomObject.prototype。为了支持dispathEvent,您必须添加CustomObject.prototype.dispatchEvent = runListener; 代码行。也可以更好地将这段代码封装到闭包函数中;

我没有在我的应用中测试过这个,但也许这可以帮助你。

更新: 下一个链接指向包含支持事件添加/删除和调度事件的XObject() 类的代码源。包括测试示例。所有代码均基于上述答案。 http://jsfiddle.net/8jZrR/

【讨论】:

  • @Dominik Kirschenhofer,请参阅 UPDATE: 段落中的链接
【解决方案3】:

我使用来自怪异的代码改进了我的示例。我仍然会将事件处理部分提取到“基类”中......也许还有更多时间=)

还有一个使用jQuery的示例!

<!doctype html>
<html lang="en">
<head>    
    <title>Custom Events Test</title>    
    <meta charset="utf-8">    
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script>    
    <script>
        /* jQuery

        var CustomObjectTextChangedEventName = 'textChanged';
        var CustomObject = function () {
            var _this = this;
            var _text = "";

            _this.ChangeText = function (newText) {
                _text = newText;
                fireTextChanged();
            };

            function fireTextChanged() {
                $(_this).trigger(CustomObjectTextChangedEventName, _text);
            }
        }

        var myCustomObject;
        $(document).ready(function () {
            myCustomObject = new CustomObject();
            $(myCustomObject).bind(CustomObjectTextChangedEventName, handleTextChanged);
        })

        function handleTextChanged(event, msg) {
            window.alert(msg);
        }

        function buttonClick() {
            var newText = document.getElementById('tbText').value;

            myCustomObject.ChangeText(newText);
        }

        */


        var CustomObjectTextChangedEventName = 'textChanged';
        var CustomObject = function (alias) {
            var _this = this;
            var _events = {};
            var _text = "";

            _this.Alias = alias;

            _this.OnTextChanged = document.createEvent("Event");
            _this.OnTextChanged.initEvent(CustomObjectTextChangedEventName, true, false);

            _this.ChangeText = function (newText) {
                var args = new TextChangedEventArgs();
                args.OldText = _text;
                args.NewText = newText;

                _text = newText;
                fireEvent(CustomObjectTextChangedEventName, args);
            };

            _this.addEventListener = function (name, handler) {
                if (_events.hasOwnProperty(name))
                    _events[name].push(handler);
                else
                    _events[name] = [handler];
            };

            _this.removeEventListener = function (name, handler) {
                /* This is a bit tricky, because how would you identify functions? 
                This simple solution should work if you pass THE SAME handler. */
                if (!_events.hasOwnProperty(name))
                    return;

                var index = _events[name].indexOf(handler);
                if (index != -1)
                    _events[name].splice(index, 1);
            };

            function fireEvent(name, args) {
                if (!_events.hasOwnProperty(name))
                    return;

                var evs = _events[name], l = evs.length;
                for (var i = 0; i < l; i++) {
                    evs[i](_this, args);
                }
            }
        }

        var TextChangedEventArgs = function () {
            var _this = this;

            _this.OldText = null;
            _this.NewText = null;
        }

        var myCustomObject;
        var myCustomObject2;
        window.onload = function () {
            myCustomObject = new CustomObject("myCustomObject");
            myCustomObject.addEventListener(CustomObjectTextChangedEventName, handleTextChanged);

            myCustomObject2 = new CustomObject("myCustomObject2");
            myCustomObject2.addEventListener(CustomObjectTextChangedEventName, handleTextChanged);
        };

        function handleTextChanged(sender, args) {
            window.alert('At ' + sender.Alias + ' from [' + args.OldText + '] to [' + args.NewText + ']');
        }

        function buttonClick() {
            var newText = document.getElementById('tbText').value;

            myCustomObject.ChangeText(newText);
        }

        function buttonClick2() {
            var newText = document.getElementById('tbText2').value;

            myCustomObject2.ChangeText(newText);
        }
    </script>
</head>
<body>
    <input type="text" id="tbText" />
    <input type="button" value="Change" onclick="buttonClick();" />

    <input type="text" id="tbText2" />
    <input type="button" value="Change" onclick="buttonClick2();" />
</body>

【讨论】:

    【解决方案4】:

    用法:jsfiddle

    这是一种幼稚的方法,但可能适用于某些应用程序:

    CustomEventTarget.prototype = {
    
        'constructor': CustomEventTarget,
    
        on:   function( ev, el ) { this.eventTarget.addEventListener( ev, el ) },
        off:  function( ev, el ) { this.eventTarget.removeEventListener( ev, el ) },
        emit: function( ev ) { this.eventTarget.dispatchEvent( ev ) }
    
    }
    
    function CustomEventTarget() { this.eventTarget = new EventTarget }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-07-02
      • 1970-01-01
      • 2015-09-07
      • 2017-05-05
      • 2022-10-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多