Create JavaScript custom event

I would like to create a custom event in JavaScript.

I have a WPF application with a WebBrowser inside, and a HTML page with JavaScript.

I work with a printer. When the state of the printer changes, it triggers an event in .NET.

Then, I call a JavaScript method OnPrinterStateChanged(state) with the InvokeScript function of the WebBrowser control.

The problem is that I have to implement the method OnPrinterStateChanged(state) in my webpage. I can't change the name of the method or subscribe/unsubscribe to the event...

I would like to move the JavaScript method OnPrinterStateChanged(state) in a separate JavaScript file.

What I want :

  • Subscribe/Unsubscribe to the event in my HTML page and decide what I want to do when the event is triggered (ex. : "function ChangeState")
  • When the .NET event is triggered, it calls the OnPrinterStateChanged(state) of my separate .js file, then the JavaScript event is triggered and the function ChangeState is called.

I found some solutions but I didn't manage to make it work... What is the simplest way to do it?

Answers:

Answer

Perhaps something like this?

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);
});

An alternative solution would be to use function composition, but then it would be hard to remove specific listeners.

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');
});

EDIT:

Here's how you can do it with 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);
});
Answer

I like using this EventDispatcher constructor method, which, using a dummy element, isolates the events so they will not be fired on any existing DOM element, nor the window or document.

I am using CustomEvent and not createEvent because it's the newer approach. Read more here.

I like to wrap each native method with these names:

on, off, trigger

function EventDispatcher(){
    // Create a dummy DOM element 
    var dummy = document.createTextNode('');

    // Create custom wrappers with nicer names
    this.off = dummy.removeEventListener.bind(dummy);
    this.on = dummy.addEventListener.bind(dummy);
    this.trigger = function(eventName, data){
        if( !eventName ) return;
        var e = new CustomEvent(eventName, {"detail":data});
        dummy.dispatchEvent(e);
    }
}

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

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

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

The above EventDispatcher could be used in another code

(for example some component)

function EventDispatcher(){
    // Create a dummy DOM element 
    var dummy = document.createTextNode('');

    // Create custom wrappers with nicer names
    this.off = dummy.removeEventListener.bind(dummy);
    this.on = dummy.addEventListener.bind(dummy);
    this.trigger = function(eventName, data){
        if( !eventName ) return;
        var e = new CustomEvent(eventName, {"detail":data});
        dummy.dispatchEvent(e);
    }
}

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

var MyComponent = function(){
    // set some default value
    this.value = 1;

    // merge `EventDispatcher` instance into "this" ('MyComponent' instance)
    Object.assign(this, new EventDispatcher()); 
}

MyComponent.prototype = {
    set : function(v){
        this.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("event type:", e.type, "  |  event value:", e.detail);
});

// set some value
comp.set(3);


See related answer to the question "Event Handler Namespace in Vanilla JavaScript"

Answer

Ok I found a solution.

I had to change the WebBrowser IE Version to Internet Explorer 11: http://weblog.west-wind.com/posts/2011/May/21/Web-Browser-Control-Specifying-the-IE-Version

And then :

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
});
Answer

Requirement: 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();

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.