创建JavaScript自定义事件

72

我想在JavaScript中创建自定义事件。

我有一个带有WebBrowser的WPF应用程序和一个包含JavaScript的HTML页面。

我使用打印机。当打印状态更改时,它会在.NET中触发一个事件。

然后,我使用WebBrowser控件的InvokeScript函数调用JavaScript方法OnPrinterStateChanged(state)

问题是我必须在我的网页中实现方法OnPrinterStateChanged(state)。我无法更改方法的名称或订阅/取消订阅事件...

我想将JavaScript方法OnPrinterStateChanged(state)移动到单独的JavaScript文件中。

我想要:

  • 在我的HTML页面中订阅/取消订阅事件,并决定当事件被触发时要执行什么操作(例如:“function ChangeState”)
  • 当.NET事件被触发时,它调用我的单独.js文件的OnPrinterStateChanged(state),然后触发JavaScript事件并调用函数ChangeState

我找到了一些解决方案,但我没有成功地使其工作...最简单的方法是什么?


你找到了什么解决方案?请展示一下你目前的尝试。 - musefan
我找到了http://www.nczonline.net/blog/2010/03/09/custom-events-in-javascript/,但像我说的,我是一个初学者,不知道它是否能做我想要的事情以及在哪里使用这段代码... - Gab
https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events - Rafael Emshoff
4个回答

110

也许像这样?

function OnPrinterStateChanged(state) {
    var evt = new CustomEvent('printerstatechanged', { detail: state });

    window.dispatchEvent(evt);
}


//Listen to your custom event
window.addEventListener('printerstatechanged', function (e) {
    console.log('printer state changed', e.detail);
});

另一种解决方案是使用函数组合,但是这样很难移除特定的监听器。

function OnPrinterStateChanged(state) {}

function compose(fn1, fn2) {
    return function () {
        fn1.apply(this, arguments);
        fn2.apply(this, arguments);
    };
}

//Add a new listener
OnPrinterStateChanged = compose(OnPrinterStateChanged, function (state) {
    console.log('listener 1');
});

//Add another one
OnPrinterStateChanged = compose(OnPrinterStateChanged, function (state) {
     console.log('listener 2');
});

编辑:

以下是如何使用jQuery实现它的方法。

function OnPrinterStateChanged(state) {
    var evt = $.Event('printerstatechanged');
    evt.state = state;

    $(window).trigger(evt);
}


//Listen to your custom event
$(window).on('printerstatechanged', function (e) {
    console.log('printer state changed', e.state);
});

谢谢您的回答!但是我遇到了这个错误:“对象不支持属性或方法'addEventListener'”。有什么想法吗?我尝试用“AttachEvent”替换它(https://dev59.com/RGct5IYBdhLWcg3wQ7bP),但我又遇到了另一个没有详细信息的脚本错误。 - Gab
在哪个浏览器中? - plalx
这是一个来自WPF的WebBrowser,因此它是Internet Explorer。 - Gab
@Gab,嗯,这个嵌入式WebBrowser似乎已经过时了,可能不支持自定义事件。我建议您使用一个微型库来支持自定义事件,并使用相同的策略。以下是一些选择:http://microjs.com/#events - plalx
好的,谢谢。如果你知道其中一个库或者如何使用Jquery实现,请告诉我...否则,无论如何还是谢谢你。 - Gab

14

我喜欢使用这个EventDispatcher构造方法,它使用一个虚拟元素来隔离事件,使它们不会在任何现有的DOM元素上触发,也不会在windowdocument上触发。

我使用CustomEvent而不是createEvent,因为这是较新的方法。 阅读更多信息.

我喜欢使用以下名称包装每个本机方法:

onofftrigger

function eventDispatcher(){
  // Create a dummy DOM element, which is a local variable 
  var dummy = document.createTextNode('')

  // Create custom wrappers with nicer names
  return {
    off(...args){ 
      dummy.removeEventListener(...args) 
      return this
    },
    on(...args){ 
      dummy.addEventListener(...args) 
      return this
    },
    trigger(eventName, data){
      if( !eventName ) return
      
      // if the event should be allowed to bubble, 
      // then "dummy" must be in the DOM
      var e = new CustomEvent(eventName, {detail:data})
      dummy.dispatchEvent(e)
      return this
    }
  }
}

////////////////////////////////////
// initialize the event dispatcher by creating an instance in an isolated way
var myEventDispatcher = eventDispatcher();

////////////////////////////////////
// listen to a "foo" event
myEventDispatcher
  .on('foo', e => console.log(e.type, e.detail) )
  .on('bar', e => console.log(e.type, e.detail) )

////////////////////////////////////
// trigger a "foo" event with some data
myEventDispatcher
  .trigger('foo', 123)
  .trigger('bar', 987);

上面的eventDispatcher可以集成到另一个代码中,例如构造函数:(例如一些组件)

function eventDispatcher(){
  var dummy = document.createTextNode('')

  return {
    off(...args){ 
      dummy.removeEventListener(...args) 
      return this
    },
    on(...args){ 
      dummy.addEventListener(...args) 
      return this
    },
    trigger(eventName, data){
      if( !eventName ) return
      var e = new CustomEvent(eventName, {detail:data})
      dummy.dispatchEvent(e)
      return this
    }
  }
}

/////////////////////////////
// Some component: 

var MyComponent = function(){
  // merge `EventDispatcher` instance into "this" instance
  Object.assign(this, eventDispatcher())
  
  // set some default value
  this.value = 1
}

MyComponent.prototype = {
  set value(v){
    this.trigger('change', v)
  }
}

var comp = new MyComponent();

// bind a listener to "comp" (which itself is an instance of "MyComponent")
comp.on('change', e => console.log("Type:", e.type,", Value:", e.detail) )

// set some value
comp.value = 3;


查看我的相关答案,其中展示了上述实现的更加复杂的实现方式。


1
不使用 ontrigger 会导致与 jQuery 不兼容吗? - Mark Kramer
1
不会冲突,因为这些方法是在“EventDispatcher”对象上声明的,它不是jQuery对象。只有在您使用“EventDispatcher”的实例扩展任何jQuery对象时才会发生冲突,但没有人会这样做,因为这毫无意义。 - vsync
完美运行,谢谢! - Mkey

2

好的,我找到了一个解决方案。

我需要将WebBrowser IE版本更改为Internet Explorer 11:http://weblog.west-wind.com/posts/2011/May/21/Web-Browser-Control-Specifying-the-IE-Version

然后:

function OnPrinterStateChanged(state) {

    var evt = document.createEvent("Event");
    evt.state = state;
    evt.initEvent("printerstatechanged", true, false);
    window.dispatchEvent(evt);

}


window.addEventListener('printerstatechanged', function (e) {
  // Do something
});

4
请记住,document.createEvent已被CustomEventEvent构造函数所取代,您应该使用它们。我相当确定IE 11支持它们。 - plalx
2
IE不支持这个。你必须使用“老派方式”来获取IE的支持。https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events - Derick
2
IE11确实支持CustomEvent @Derick - vsync
Mozilla为不支持CustomEvent(基本上只有IE)的浏览器提供了一个简单的Polyfill - Kyle Fox

2

要求:ES6

let MyClass = (function () {
    let __sym = Symbol('MyClass');

    class MyClass {
        constructor() {
            this[__sym] = {};
        }
        on(event, callback) {
            this[__sym][event] = { callback: callback }
        }
        getError() {
            let event = this[__sym].error;

            if (event && event.callback) {
                event.callback('some parameter');
            }
        }
    }

    return MyClass;
}());

let myClass = new MyClass();

myClass.on('error', function (e) {
    console.log('error:', e);
});

console.log('before getting error:');

myClass.getError();


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接