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
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.
is there any way, how can I globally (in service) disable and enable all ng-click and ng-submit events?
For example when user is offline I want to disable all actions till he gets connection back..
I tried to bind all elements with an onClick event which will call stopImmediatePropagation but it didn't work..
$('*[ng-click]').click(function( event ) {
event.stopImmediatePropagation();
});
Also this question is a little bit different from this one:
Disable ng-click on certain conditions of application for all types of element
I'd like to disable/enable all events in APP globally from service, I'm not able to modify all ng-* calls on all elements in the APP..
Try including a return false too:
$('*[ng-click]').click(function( event ) {
event.stopImmediatePropagation();
return false;
});
Snippet
The below snippet demonstrates that multiple event handlers attached to a single <a> works too.
$(function () {
$("a").click(function () {
alert("Hello!");
return false;
});
$("a").click(function () {
alert("Bye!");
return false;
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
Click Me
So finally I end up with temporarily disabling all events on the page using jquery..
I got inspired from this plugin http://ignitersworld.com/lab/eventPause.html which for some reason did not work (without any error)
So I took main parts and put it to this class which is working now using jquery v2.1.1:
var EventManager = function() {
var self = this;
var nullFun=function(){};
var getIndex = function(array,value){
for(var i=0; i< array.length; i++){
if(array[i]==value){
return i;
}
}
return -1;
};
this.pauseEvent = function(elm,eventAry){
var events = $._data(elm, "events");
if (events) {
$.each(events, function(type, definition) {
if((getIndex(eventAry,type)!=-1)||(eventAry=='')){
$.each(definition, function(index, event) {
if (event.handler.toString() != nullFun.toString()){
if(!$._iwEventPause) $._iwEventPause = {};
$._iwEventPause["iw-event" + event.guid] = event.handler;
event.handler = nullFun;
}
})
}
})
}
};
this.activeEvent = function(elm,eventAry){
var events = $._data(elm, "events");
if (events) {
$.each(events, function(type, definition) {
if((getIndex(eventAry,type)!=-1)||(eventAry=='')){
$.each(definition, function(index, event) {
if (event.handler.toString() == nullFun.toString()){
event.handler = $._iwEventPause["iw-event" + event.guid];
}
})
}
})
}
};
this.disableAll = function(el) {
el = el || $('*');
el.each(function() {
self.pauseEvent($(this)[0], '');
});
self.pauseEvent($(window)[0], '');
};
this.enableAll = function(el) {
el = el || $('*');
el.each(function() {
self.activeEvent($(this)[0], '');
});
self.activeEvent($(window)[0], '');
};
return this;
};
var eManager = new EventManager();
eManager.disableAll();
eManager.enableAll();
This will go through window object and all elements on the page, move their event handlers away to _iwEventPause object and replace handlers with dummy function.. When enabling, it will move handlers back so they get normally called..
This solution does not handle event handlers added after disabling..
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'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.
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);
});