there! i have a problem when creating one function just like JQUERY does.
here is the action : https://jsfiddle.net/77yzLt6v/
one time execution event
HTML :
<div id="justOnce">click me!</div>
function one(dom, event, callback) {
dom.addEventListener(event, function(e) { // add event
this.removeEventListener(event, callback); // remove it
});
}
one(document.getElementById("justOnce"), "click", function() {
alert("this alert only show once time");
});
what's wrong with my code?
thanks in advance...
Your code binds an event handler that removes callback as an event handler.
The only problem is … you never bound callback as an event handler in the first place.
You want something more like this:
function one(dom, event, callback) {
function handler(e) {
callback.call(this, e);
this.removeEventListener(event, handler);
}
dom.addEventListener(event, handler);
}
i.e.
You need to call the callback
You need to remove the event handler you actually bound
New standard supports this, but don't work in any browser yet (caniuse.com)
window.addEventListener('resize', console.log, {once: true})
polyfill:
;(function(){
let p = document.createElement('p')
let count = 0
let event = new CustomEvent('x')
p.addEventListener('x', () => {i++})
p.dispatchEvent(event)
p.dispatchEvent(event)
if (i != 2) return
for (let obj of [Window, Element]) {
let orig = obj.prototype.addEventListener
function addEventListener(type, callback, opts) {
let args = Array.from(arguments)
if(opts && opts.once) {
function handler(e) {
callback.call(this, e)
this.removeEventListener(type, handler)
}
args[1] = handler
}
orig.apply(this, args)
}
obj.prototype.addEventListener = addEventListener
}
}())
Related
How do I control only firing an event once?
Actually, a quick Google appears to allude to the fact that .one helps..
Use once if you don't need to support Internet Explorer:
element.addEventListener(event, func, { once: true });
Otherwise use this:
function addEventListenerOnce(target, type, listener, addOptions, removeOptions) {
target.addEventListener(type, function fn(event) {
target.removeEventListener(type, fn, removeOptions);
listener.apply(this, arguments);
}, addOptions);
}
addEventListenerOnce(document.getElementById("myelement"), "click", function (event) {
alert("You'll only see this once!");
});
https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener
http://www.sitepoint.com/create-one-time-events-javascript/
https://www.webreflection.co.uk/blog/2016/04/17/new-dom4-standards
You can use jQuery's one method, which will subscribe to only the first occurrence of an event.
For example:
$('something').one('click', function(e) {
alert('You will only see this once.');
});
Same as rofrol's answer, just another form:
function addEventListenerOnce(element, event, fn) {
var func = function () {
element.removeEventListener(event, func);
fn();
};
element.addEventListener(event, func);
}
Just use proper option in your addEventListener method call:
element.addEventListener(event, func, { once: true })
Slightly improved version of rofrol's anwser:
function addEventListenerOnce(target, type, listener) {
target.addEventListener(type, function fn() {
target.removeEventListener(type, fn);
listener.apply(this, arguments);
});
}
By using apply all arguments are passed and the this works as expected.
Additionally, you can do this:
window.addEventListener("click", function handleClick(e) {
window.removeEventListener("click", handleClick);
// ...
});
Added the options for add/remove event listener:
function addEventListenerOnce(target, type, listener, optionsOrUseCaptureForAdd, optionsOrUseCaptureForRemove) {
const f = event => {
target.removeEventListener(type, f, optionsOrUseCaptureForRemove);
listener(event);
}
target.addEventListener(type, f, optionsOrUseCaptureForAdd);
}
I am trying to remove some listeners attached to certain array of elements.
As I am adding the event listeners I cannot get a reference to the parameters I needs. I have been doing some research and found out that can be solved using closures, but I can't figure out very well how to do it
here is my function reference
const editTask = function (element, taskIndex) {
const handler = function(event) {
// my code ...
}
}
and this is how I am adding the listeners
function addEditListeners() {
const editButtons = [].slice.call(document.getElementsByClassName("edit-btn"));
console.log('editbuttons', editButtons);
//editButtons.forEach(function (element) {
// element.removeEventListeners("click", editTask);
//});
editButtons.forEach(function (element, index) {
element.addEventListener("click", handler);
});
}
I have tried sending the parameters in parenthesis but the editTask is undefined, what am I doing wrong?
Notice that you are passing handler which only exists in the editTask function and nowhere else, your listener adding function should be like this
editButtons.forEach(function (element, index) {
element.addEventListener("click", (e) => editTask(element, index, e));
});
Also I see you want an event in there as well so you should pass it in edit task
const editTask = function (element, taskIndex, event) {
const handler = function(event) {
// my code ...
}
handler(event);
}
When I bind a function with the parent this passed in thisArg, I can't unbind the same named function expression, but without it I can unbind the function itself. Why is that?
This works:
choicesList.addEventListener("click", function() {
const self= this;
document.addEventListener("click", function checkClick(e) {
if (!e) e = event;
if (!self.contains(e.target)) {
document.removeEventListener("click", checkClick);
}
}, false);
});
This doesn't:
choicesList.addEventListener("click", function() {
document.addEventListener("click", function checkClick(e) {
if (!e) e = event;
if (!this.contains(e.target)) {
document.removeEventListener("click", checkClick);
}
}.bind(this), false);
});
The reason for this is issue is that calling bind() on a function returns a new instance of that function:
function someHandler() {
alert('hi');
}
const someHandlerBinded = someHandler.bind(document);
// Returns false, seeing as these are different instances of the function
console.log( someHandlerBinded === someHandler );
By setting an event handler directly, via the result of bind() as you are in your second block of code, this causes a new instance of that function handler to be passed to addEventListener(). This in turn means that the subsequent attempt to removing this handler on line:
document.removeEventListener("click", checkClick);
will fail, seeing that the the defined function checkClick is not the same as the actual handler function used for that click event (ie the new function instance returned from function checkClick(){ ... }.bind())
One way to resolve this might be the following:
choicesList.addEventListener("click", function() {
// Declare the bound version of the click handler
const boundClickHandler = function checkClick(e) {
if (!e) e = event;
if (!this.contains(e.target)) {
// Removing the result of bind, rather than the declared
// checkClick handler
document.removeEventListener("click", boundClickHandler);
}
}.bind(this)
// Adding the result of bind as you currently are doing
document.addEventListener("click", boundClickHandler, false);
});
It's because this is in a function that is nested within another function and the nested function doesn't have the same invocation context as the outer one. The first one works because you are caching the object that the outermost this is referencing and you are then able to correctly reference it in the inner function.
You can read more about the volatility of this here.
EDIT : I need to invoke one function which fetches data and reloads the page's contents. But this has to be invoked once another function has fetched data(webSql). I cannot use the WebSql callback as variables are out of scope. So I created a custom Event and added a listener in the second function scope. So when data is fetched I am dispatching the event in the first function scope. Now the issue if the page was reloaded more than once, listeners will get added multiple times and all will be invoked which I dont want.
I need to make sure that only one function is listening to a custom event. Right now am removing the listener once its invoked like this :
document.addEventListener("customEvent", function () {
actualCallBack(var1, var2); // Since I need to pass parameters I need to use callBack within an anonymous function.
this.removeEventListener("customEvent", arguments.callee, false);
}, false);
But the problem is anonymous function will be removed only after its invoked in the first place. There is a possibility of listener getting added mulitple times. How can I remove event listeners before adding a new one ?
document.removeEventListener("customEvent");
document.addEventListener(...);
I could have removed it, if a variable function was used instead, but I need to pass some parameters to callback so I need to use anonymous functions.
using felix's suggestion
var setSingletonEventListener = (function(element){
var handlers = {};
return function(evtName, func){
handlers.hasOwnProperty(evtName) && element.removeEventListener(evtName, handlers[evtName]);
if (func) {
handlers[evtName] = func;
element.addEventListener(evtName, func);
} else {
delete handlers[evtName];
}
};
})(document);
setSingletonEventListener("custom event", function(){
});
//replaces the previous
setSingletonEventListener("custom event", function(){
});
//removes the listener, if any
setSingletonEventListener("custom event");
Here's one way:
var singletonEventListenerFor = function (element, eventName) {
var callback = null;
element.addEventListener(eventName, function () {
callback && callback();
});
return function (set) {
callback = set;
};
};
Testing:
var event = document.createEvent("Event");
event.initEvent("customEvent", true, true);
var listener = singletonEventListenerFor(document, "customEvent");
var counter = 0;
listener(function () {
console.log(++counter);
});
// counter === 1
document.dispatchEvent(event);
// Remove the listener
listener();
// This shouldn't increment counter.
document.dispatchEvent(event);
listener(function () {
console.log(++counter);
});
// counter === 2
document.dispatchEvent(event);
// counter === 3
document.dispatchEvent(event);
console.log('3 === ' + counter);
http://jsfiddle.net/Dogbert/2zUZT/
API could be improved by returning an object with .set(callback) and .remove() functions instead of using a single function to do both things, if you like.
Store somewhere that you've already applied the listener, and only add it, if it hasn't been added already.
I have a Javascript object that registers for the mouse move event. But my problem is that the event parameter is not being passed in my custom function, its always undefined.
If you look inside the function touchMove() below you will see that the parameter event is always undefined for some reason?
What am I doing wrong?
MyObject.prototype.registerPointerEvents = function()
{
var instance = this; // function() { instance.func(); }
var ele = this.getAttrib("divEle");
ele.addEventListener("mousemove", function() { instance.touchMove(); }, false);
}
MyObject.prototype.touchMove = function( /*Event*/ event )
{
// Virtual function to be overridden in child classes
console.log("MOVE 1: "+event); // here it outputs event is undefined
if (!event)
event = window.event;
console.log("MOVE 2: "+event); // here event is still undefined
if (this.getAttrib("isDragging") == "true")
{
console.log("MOVE TRUE");
this.getAttrib("divEle").style.left = event.clientX+"px";
this.getAttrib("divEle").style.top = event.clientY+"px";
}
}
Of course is undefined, you are calling an anonymous function without the parameter.
Better use this:
ele.addEventListener("mousemove", instance.touchMove, false);
That is (almost) the same as
ele.addEventListener("mousemove", function(e) { instance.touchMove(e); }, false);