Does calling a javascript function multiple time cause any unwanted issues? - javascript

So how does the javascript interpreter handle multiple functions calls that are the same? Does it override the previous, or cause some kind of clutter?
I have written functions in the console, the newest one seems to always work and override the old one, I am always afraid the old functions are stored away, which I assume can cause performance issues/bloat.
So for example.
$('body').on('click', function() { alert('hello') });
Will alert hello, then I rewrite the same function alerting hello again
$('body').on('click', function() { alert('hello') });
What happens? Does it replace it, create a new copy?

Yes, the event listener will remain active even if you bind more events to the same element, even if the event is identical to the previously bound event.
You can observe this behaviour in this jsfiddle.
// Example with anonymous function - this will print 'hello' twice
$('#btn1').on('click', function() { alert('hello') })
$('#btn1').on('click', function() { alert('hello') })
// Example with named function - this will work exactly the same
function sayHello () {
alert('hello')
}
$('#btn2').on('click', sayHello)
$('#btn2').on('click', sayHello)
To remove previously bound listeners, you must use the removeEventListener() method or its jQuery alternative, unbind().

Related

Why can’t I bind a function () to the onload event and the resize event the same way?

Why can’t I bind a function () to the onload event and the resize event the same way?
I want to bind my own functions to javascript’s onresize and onload events.
This is test code in a .JS file.
I made it work by using two different ways to bind my functions to the events.
And it works well.
window.addEventListener('resize', function() {
alert (“Hi from resize”);
}
window.onload = function () {
alert (“Hi from onload”);
}
However, when I try to use the same way to bind them, one always fails. I.e. Neither of these work:
window.resize = function () {
alert (“Hi from resize”);
}
window.addEventListener('onload', function() {
alert (“Hi from onload”);
}
I’ve found endless posts on how to make specific examples work one way or the other.
But I can’t find anything that hints at why the same way won’t work for both.
Can anyone help me figure this out:
--- Someone wrote that addEventListener () is the preferred. And that direct assignment was not. Is that true?
--- Should direct assignment still work for both events?
--- Should addEventListener () work for both?
--- Am I missing something else that I should know?
I mean the code I have works fine. It’s just inconsistent, and that always seems to indicate I’ve screwed up somewhere.
And I guess if either way is "Supposed To" work for both events I can go back and search for typos. But it would help to know which way is preferred, if either. Or I'm missing something important.
Thank you.
You have it backwards in your second block. When you're assigning to the window property, the name is on<eventname>; when you're using addEventListener() you just use <eventname>. So it should be:
window.onresize = function() {
alert("Hi from resize");
};
window.addEventListener("load", function() {
alert("Hi from resize");
});
addEventListener is preferred these days for a few reasons:
You can have multiple listeners, which all execute when the event occurs. When you assign to a property, it replaces the previous value. If you want to remove an event listener, you can use removeEventListener selectively (this requires binding to a named function, because you need to pass the same function when removing).
It can be used with custom events defined by the application; onXXX properties can only be used with standard events.
addEventListener is preferred because if you assign you will override any other event that has been assigned to that event.
the event for onload when using addEventListener is just "load"

is this a valid way to define an explicit function inside addEventListener?

Just wondering, is this a valid way to define an explicit function inside JavaScript's addEventListener function so that it could be removed at any time using removeEventListener?
var somefunction;
window.addEventListener('load', somefunction = function(){
//do something
}, false);
window.removeEventListener('load', somefunction, false);
In other words, is it ok to define a variable somefunction and then assign an anonymous function to it inside addEventListener, instead of defining somefunction outright from the get go? It seems to work in FF and Chrome, but just wanna make sure this is officially valid JavaScript syntax.
Yes, it will work. An assignment is an expression -- it assigns to the variable and also returns the value that it assigned.
Personally I think this is a confusing way to write it. If you're refer to the function by name, put the definition where you define the name, not where you use it. In particular, if you try to do this twice, you'll have a problem because each event listener will have a different function, but it has the same name.
window.addEventListener('event1', somefunction = function() {
//do something
});
window.addEventListener('event2', somefunction = function() {
//do something
});
Now you can only remove event2, because somefunction no longer refers to the function that was added to event1.
Calling removeEventListener() with arguments that do not identify any
currently registered EventListener on the EventTarget has no effect.
So as long as removeEventListener has say a 'click' event as an argument, any one eventListener registered to the 'click' event will be removed. This is evident in OP's case, therefore it is feasible according to the criteria previously mentioned.
The following Snippet demonstrates a registered eventListener added to #target1 to listen for the 'click' event. It will be functional until removeEventListener() is called to remove the eventListener within 4 seconds. Notice that this particular removeEventListener's arguments are:
the event object............: click
a named function..........: eventLog()
and it's capture boolean: false
The identifying argument is 'click' and the target.event is #target that allows removeEventListener() to identify it's target.
SNIPPET
var eventLog;
var tgt1 = document.getElementById('target1');
var term = document.getElementById('btn');
tgt1.addEventListener('click', eventLog = function(e) {
console.log('target1 has been clicked');
}, false);
setTimeout(function() {
tgt1.removeEventListener('click', eventLog, false);
eventLog('Target1 eventListener is removed');
}, 4000);
function eventLog(str) {
console.log(str);
}
#target1 {
border: 2px solid red;
}
<p>Start clicking TARGET1 several times and you'll notice that each `click` event is firing as displayed in the console. Within 4 seconds, TARGET1's eventListener should be removed.</p>
<div id='target1'>TARGET1</div>

Javascript addEventListener and removeEventListener with partial function

I want to create javascript function adds an event listener to a div such that when the div is clicked, it runs a function, and ensures that the div can only be clicked once. My function looks like this right now:
function clickOnce(divID,func){
var args = Array.prototype.slice.call(arguments,2);
functionToRun=partial(func,args);
thisDiv=document.getElementById(divID);
thisDiv.addEventListener("click",function(e){
e.target.removeEventListener(e.type, arguments.callee);
functionToRun();
});
}
I have a working example in jsfiddle: https://jsfiddle.net/qtzLhgr4/1/
In the JSfiddle, when I click button 1 it says "button #2 clicked".
You didn’t declare functionToRun, so it’s implicitly global. Declare it and thisDiv:
var functionToRun=partial(func,args);
var thisDiv=document.getElementById(divID);
Then start working in strict mode, which will prevent these kinds of problems as well as stop you from using arguments.callee, which is the Wrong Way To Do Things:
thisDiv.addEventListener("click", function listener(e) {
thisDiv.removeEventListener("click", listener);
functionToRun();
});
(e.target was also incorrect; this would have been better.)
functionToRun (and thisDiv, but that's not related to the problem) is global. The second call to clickOnce overwrites functionToRun, so both event handlers execute the same function.
Always declare your variables. Also note that arguments.callee is deprecated. You can use a named function expression instead.

Javascript closure behaves differently when bound to an event

I am trying to use a closure to ensure that a function can only execute once. Sounds simple, and it works like this:
function runOnce(fn) // returns copy of fn which can only execute once
{
var ran = false;
return function()
{
if (!ran)
{
fn();
ran = true;
}
};
}
I have tested the function like so:
function lazyLoadGrid(event, ui)
{
alert('hi');
}
var test1 = runOnce(lazyLoadGrid);
var test2 = runOnce(lazyLoadGrid);
test1();
test2();
test1();
test2();
And it works as expected - 'hi' gets alerted exactly twice.
But then I try to use runOnce(lazyLoadGrid) as the callback to a jQuery UI event:
$('.accordion').each(function()
{
$(this).accordion({ autoHeight: false, change: runOnce(lazyLoadGrid) });
});
And madness ensues. What I expect is that each 'accordion' on the page will run lazyLoadGrid() exactly once, when that accordion is first opened. Instead, the closure callbacks seem to behave as if they are all referencing the same copy of 'ran'. lazyLoadGrid() runs the first time I open any accordion, and then never runs again for any other accordion. Logging the pre-condition value of 'ran' shows that it's 'true' every time I click any accordion after the first one.
What is the explanation for this? It may be worth noting I have an odd page, with nested accordions, and multiple jQuery UI tabs each containing accordions. To make matters worse, when I switch tabs the closure actually does run on the first-opened accordion of any given tab. Any advice is much appreciated.
The problem:
I believe the trouble you are having is because what you are thinking of as an "accordion" is actually a "panel". The accordion consists of all the panels in a group. It sounds like you want to run it once per panel, not once per accordion. The following demo illustrates the concept by including two accordions on a page. Notice that lazyLoadGrid() is run twice, once for each accordion:
http://jsfiddle.net/cTz4F/
The solution:
Instead what you want to do is create a custom event and call that event on each panel. Then you can take advantage of jQuery's built-in .one() method which causes that an event handler is called exactly once for each element:
$('.accordion').accordion({
autoHeight: false,
change: function(e, ui) {
ui.newHeader.trigger("activated");
}
});
$('.accordion > h3').one("activated", lazyLoadGrid);
Working demo: http://jsfiddle.net/cTz4F/1/
How about:
function runOnce(fn) {
return function(){
fn();
fn = function(){};
}
}
// test
var foo = function(){
console.log('bar');
}
foo = runOnce(foo);
foo(); // bar
foo();
foo();
i think because the function expects the event parameter to be specified.
try this:
$('.accordion').each(function() {
$(this).accordion({ autoHeight: false, change: runOnce(arguments[0],lazyLoadGrid) });
});
try using directly the function lazyLoadGrid or if you have to use the runOnce you have to specify the arguments[0] (which is the event) as a parameter in the function
-- edit --
sorry i forgot to put the event in the function

Clearing a jquery document.ready() call

How do I clear out anonymous functions that are set to trigger via a jQuery document.ready() call?
For example:
<script type="text/javascript">
//some code sets a doc ready callback
$(document).ready(function ()
{
alert('ready');
});
//my attempt to prevent the callback from happening
window.onload = null;
$(document).unbind("ready");
</script>
The alert happens regardless of my attempts to circumvent it. Is there any way to do this?
You'd probably get the most appropriate answer if you described what problem you're really trying to solve.
jQuery doesn't have a publicly documented way to undo or block document.ready() handlers. If you control the code, you can use a global variable and a conditional like this:
var skipReady = false;
$(document).ready(function ()
{
if (!skipReady) {
alert('ready');
}
});
// skip the document.ready code, if it hasn't already fired
skipReady = true;
Or, if you want to hack into jQuery a bit (beyond the documented interfaces), you can do this:
$(document).ready(function() {
alert("ready");
});
// stop the ready handler
$.isReady = true;
You can see this last one work here: http://jsfiddle.net/jfriend00/ZjH2k/. This works because jQuery uses the property: $.isReady to keep track of whether it has already fired the ready handlers or not. Setting it to true makes it think it has already fired them so it won't every do it again.
This works:
$(document).bind("ready", function () { alert("hey!"); });
$(document).unbind("ready");
Seems like a bug to me - all other events in jQuery are able to be unbound. Omitting this one is inconsistent.
Not a direct answer as to the omission, but here's some related info from jQuery docs:
All three of the following syntaxes are equivalent:
$(document).ready(handler)
$().ready(handler) (this is not recommended)
$(handler)
There is also $(document).bind("ready", handler). This behaves similarly to the ready method but with one exception: If the ready event has already fired and you try to .bind("ready") the bound handler will not be executed. Ready handlers bound this way are executed after any bound by the other three methods above.
$(document).ready() is dependent on the onLoad event which is triggered by the browser meaning you can not prevent it from happening. If the alert() is determined by some condition then I would use an if/else statement to decide whether it is called.
Super old question, but came across the need to do this recently to prevent document.ready code I didn't control from running in certain instances. This can be achieved by proxying jQuery's ready function, rather like a test spy. The following will work:
var ready = $.prototype.ready;
// proxy the ready function
$.prototype.ready = function ( fn, allowed ) {
allowed = allowed || false;
if ( allowed ) {
ready.call( this, fn );
}
};
All calls to $( document ).ready will now be ignored. You can override this behaviour by passing true as the second argument: $( document ).ready( fn, true )

Categories