I'm creating a custom WinJS control with an event listener. For simplicity, this example should fire an event whenever it is tapped.
This is created with the markup:
<div class="alphaNavBar" data-win-control="MobMan.Controls.AlphaNavBar"></div>
The control is implemented here. It throws an "invalid argument" exception at the dispatchEvent(...) line.
(function () {
var alphaNavBar = WinJS.Class.define(function (el, options) {
// Create control
var self = this;
this._element = el || document.createElement("div");
this._element.winControl = this;
this._element.innerText = "Hello World!";
this._selection = false;
// Listen for tap
this._element.addEventListener("MSPointerDown", function (evt) {
// Toggle selection
self._selection = !self._selection;
// Selection changed, fire event
// Invalid argument here
self._element.dispatchEvent("mySelectionChanged", { selection: self._selection });
// Invalid argument here
});
});
// Add to global namespace
WinJS.Namespace.define("MobMan.Controls", {
AlphaNavBar: alphaNavBar
});
// Mixin event properties
WinJS.Class.mix(MobMan.Controls.AlphaNavBar, WinJS.Utilities.createEventProperties("mySelectionChanged"), WinJS.UI.DOMEventMixin);
})();
This event is listened to by:
var alphaNavBar = document.querySelector(".alphaNavBar");
alphaNavBar.addEventListener("mySelectionChanged", function (evt) {
// Should fire when alphaNavBar is tapped
debugger;
});
What am I doing wrong here?
I posted my question here as well and got an answer modifying the event dispatch like so:
// Listen for tap
this._element.addEventListener("MSPointerDown", function (evt) {
// Toggle selection
this._selection = !this._selection;
// Create the event.
var _event = document.createEvent('customevent');
// Define that the event name is 'mySelectionChanged' and pass details.
_event.initCustomEvent('mySelectionChanged', true, true, { selection: this._selection });
// Selection changed, fire event
this.dispatchEvent(_event);
});
This was able to trigger the event correctly for me. Still not sure what I was doing wrong before, but it is fixed now.
Related
I am currently working on a javascript module which open and close boxes, tooltip or similar, the function works great the only problem is when I call it twice on a page where the 'boxes' classes are different the window mouseup event will be overwritten and only one of the two module instances of boxes can now be closed after opening them.
var boxRevealer = (function () {
var buttons;
var boxes;
var element;
var drp_active = false;
var boxConstruct = function (btns, bxs) {
buttons = document.querySelectorAll(btns);
boxes = document.querySelectorAll(bxs);
boxEvents();
};
var boxEvents = function () {
buttons.forEach(function (e) {
e.addEventListener("click", function (ee) {
element = document.getElementById(e.getAttribute("data-drp"));
element.classList.toggle("displayn");
drp_active = true;
});
});
window.addEventListener("mouseup", function (e) {
if (drp_active === true) {
if (!e.target.classList.contains("filt_holy")) {
boxes.forEach(function (e) {
console.log("ELEMENT");
console.log(e);
e.classList.add("displayn");
});
}
}
}, false);
};
return {
boxConstruct: boxConstruct,
boxEvents: boxEvents
};
})();
Here is how i call the module
window.addEventListener("load", function(e){
boxRevealer.boxConstruct(".head_drp_btn", ".head_drp");
boxRevealer.boxConstruct(".mkt_drp_btn", ".mkt_drp");
});
So my question is, should I always name the boxes the same, or is there a work around?
Just remove the event before adding it, I think the same event is getting called twice.
So updated code will be as follows:
// Attach an event handler to <div>
e.addEventListener("mousemove", myFunction);
// Remove the event handler from <div>
e.removeEventListener("mousemove", myFunction);
And remove the window event as well before adding it.
I'm creating a custom event in plain Javascript and attaching it to a DOM element. The element has multiple event listeners for the same event. The code I've written is:
var element = document.getElementById('demo');
element.addEventListener("myCustomEvent", function(e) {
console.log("myCustomEvent first handler triggered");
});
element.addEventListener("myCustomEvent", function(e) {
console.log("myCustomEvent second handler triggered");
e.data['otherKey'] = 'other value';
return e.data;
});
// Trigger the event
var event = new CustomEvent("myCustomEvent", {
data: {
firstKey: 'first Value'
}
});
element.dispatchEvent(event);
Now what I want is, get the data from last event handler like below:
var modifiedData = element.dispatchEvent(event);
Though I know the above line may not be correct, but I want something like that. If I use jQuery, there is pretty simple thing like $.triggerHandler I can use that iterates over all the listeners for a particular event and gives the return value of last handler if any.
But I want to do it in pure Javascript. Is there any solution for such kind of thing?
Just use the detail property and it should work just as you'd expect:
var element = document.getElementById('demo');
element.addEventListener("myCustomEvent", function(e) {
console.log("myCustomEvent first handler triggered");
e.detail.otherKey = 'second value';
});
element.addEventListener("myCustomEvent", function(e) {
console.log("myCustomEvent second handler triggered");
console.log(e.detail);
});
// Trigger the event
var event = new CustomEvent("myCustomEvent", {
detail: {
firstKey: 'first Value'
}
});
element.dispatchEvent(event);
<div id="demo"></div>
Per comments, here's an idea how you could achieve what you're hoping to do:
var element = document.getElementById('demo');
var originalFun = element.__proto__.addEventListener.bind(element);
var handlers = {};
var wrapperFun = function(e) {
if (e.type in handlers) {
var data = e.detail;
handlers[e.type].forEach(function(fun){
data = fun(data) || data;
});
}
};
element.__proto__.addEventListener = function(type, handler) {
if (typeof handlers[type] === 'undefined') {
handlers[type] = [];
originalFun(type, wrapperFun);
}
handlers[type].push(handler);
};
element.addEventListener("myCustomEvent", function(e) {
console.log("myCustomEvent first handler triggered");
e.otherKey = 'second value';
return e;
});
element.addEventListener("myCustomEvent", function(e) {
console.log("myCustomEvent second handler triggered");
console.log(e);
});
// Trigger the event
var event = new CustomEvent("myCustomEvent", {
detail: {
firstKey: 'first Value'
}
});
element.dispatchEvent(event);
<div id="demo"></div>
The easiest way is to save all Event listener results to some object.
Like const eventObj = {};
So, after each event you can just Object.assign(eventObj, <yourDataFromEvent>
More about Object.assign() here
I have been writing a plugin, and i really like this format
Function.prototype._onClick = function() {
// do something
}
Fuction.prototype.addListner = function() {
this.$element.on('click', this._onClick.bind(this));
}
the problem is sometimes i need the element being clicked and the main object. Doing as below i loose the dom element and not using bind looses the main object.
Fuction.prototype.addListner {
this.$element.find('.some-class').on('click', this._onClick.bind(this));
}
To achieve that i go back to ugly version
Fuction.prototype.addListner = function() {
var self = this;
this.$element.find('.some-class').on('click', function() {
self._onClick($(this));
});
}
Is there any better way to do this?
As zerkms, you can use the event.target to achieve what you want.
When using .on, the handler is :
handler
Type: Function( Event eventObject [, Anything extraParameter ] [, ...
] ) A function to execute when the event is triggered. The value false
is also allowed as a shorthand for a function that simply does return
false.
So your _onClick function will receive click event as its 1st parameter, then from event.target, you can now get the clicked item.
var Test = function(sel) {
this.$element = $(sel);
this.value = 'My value is ' + this.$element.data('val');
};
Test.prototype.addListner = function() {
this.$element.find('.some-class').on('click', this._onClick.bind(this));
}
Test.prototype._onClick = function(evt) {
// Get the target which is being clicked.
var $taget = $(evt.target);
//
console.log(this.value);
// use $target to get the clicke item.
console.log($taget.data('val'));
}
var test = new Test('#test');
test.addListner();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<div id="test" data-val="divVal">
<button class="some-class" data-val="Button-A">btnA</button>
<button class="some-class" data-val="Button-B">btnB</button>
</div>
I would like to convert the event handler to a jquery style click event but it doesnt seem to like passing the event through, perhaps its because its not an anonymous function anymore?
// variables
var faqOne = document.getElementById("faqOne");
var $hiddenOne = $(".faqOneHidden");
// javascript event handler works!
faqOne.addEventListener("click", function(e){
showFaqOne.showClickedFaq(e);
}, false);
// javascript event handle - doesnt work!
$("#faqOne").click(function(){
showFaqOne.showClickedFaq(e);
});
// constructor
function DisplayQFaqs(link, faq){
this.link = link;
this.faq = faq;
}
// method prototype
DisplayQFaqs.prototype.showClickedFaq = function(e){
var el = e.currentTarget;
if(el === this.link) {
this.faq.toggle("slow", function(){
});
}
};
// new DisplayQFaqs Objects
var showFaqOne = new DisplayQFaqs(faqOne,$hiddenOne);
Your e is undefined inside
$("#faqOne").click(function(){
showFaqOne.showClickedFaq(e);
});
Change it to
$("#faqOne").click(function(e){//Now e is there
showFaqOne.showClickedFaq(e);
});
I have built a dom object Engine that has private/public fields/methods that I have simplified below:
function Engine(args){
this.display = args.display;
this.getDisplay = function(){return this.display;}
this.alertMsg = function(msg){
console.log(this.display);
alert(msg);
}
}
What I would like to do is build a custom event that would be triggered after the alert(msg) such as $(this.display).trigger("afterAlert");
function Engine(args){
this.display = args.display;
this.getDisplay = function(){return this.display;}
this.alertMsg = function(msg){
console.log(this.display);
alert(msg);
// trigger custom event here
$(this.display).trigger("afterAlert");
}
}
Now, this event could be empty or not. How would one or more objects declared later register to the "afterAlert" event? In my case, additional javascript files are loaded by the main file dynamically and could contain a code ressembling :
function new_obj(){
bind("afterAlert", function(){
alert("alert was called");
});
}
See my answer from this question...repeated for clarity
I will tackle the register, triggering and unbinding of custom events.
jQuery has all the tools you need to register, bind and unbind to custom events.
Below is an example of hooking up two divs to a custom event called customAjaxStart. I can then trigger this function and both handlers will get called.
Quick Demo Here - Have the firebug/ie8 console enabled.
e.g
$( function() {
$('#div1').bind('customAjaxStart', function(){
console.log('#div1 ajax start fired');
$(this).fadeTo('slow', 0.3);
});
$('#div2').bind('customAjaxStart', function(){
console.log('#div1 ajax start fired');
$(this).fadeTo('slow', 0.3);
});
//fire the custom event
$.event.trigger('customAjaxStart');
//unbind div1 from custom event
$('#div1').unbind('customAjaxStart');
//again trigger custom event - div1 handler will not fire this time
$.event.trigger('customAjaxStart');
});
Taking the above as an example I would trigger the customAjaxStart from the global ajaxStart. Any listeners would be triggered automatically whenever an xhr call is about to be made (ideal for disabling your widgets or showing a loading gif etc) e.g
$.ajaxStart( function(){
$.event.trigger('customAjaxStart');
});
I think what you are looking for is the Observer pattern. At least that's how I would implement it. The following code snippet uses different names but it does essentially what you want (allows registering for events, and even triggering):
Observable = {
addObserver: function(observer) {
if (!this.__observers) this.__observers = [];
this.__observers.push(observer);
},
addGlobalObserver: function(observer) {
if (!this.__global_observers) this.__global_observers = [];
this.__global_observers.push(observer);
},
removeObserver: function(observer) {
var newObservers = [];
var co;
while (co = this.__observers.pop()) {
if (co != observer) newObservers.push(co)
}
this.__observers = newObservers;
newObservers = [];
while (co = this.__global_observers.pop()) {
if (co != observer) newObservers.push(co)
}
this.__global_observers = newObservers;
},
notify: function(event) {
var allObservers = this.__global_observers.concat(this.__observers);
for (var i=0; i < allObservers.length; i++) {
var o = allObservers[i];
if (o[event]) {
var args = []
for (var j=1; j < arguments.length; j++) {
args.push(arguments[j])
};
o[event].apply(this, args);
}
};
},
__global_observers: [],
__initializer: function() {
this.__observers = [];
}
};
If you include this code into your class, you can register for events using addObserver() (addGlobalObserver() for "class level" events). Inside the object you trigger an event using notify().
Code taken from Coltrane.