I'm trying to write some Javascript that when a div is clicked, a function is called with the parameter of that divs ID, the function works when I just send the hardcoded div id like so:
$('#areaOne').on('show.bs.collapse', onSectionClick('#areaOne'));
function onSectionClick(){
var x = $('#areaOne).hasClass('toggled') ? 'false' : 'true';
console.log(x)
}
However when I try it like this:
$('#areaOne').on('show.bs.collapse', onSectionClick('#areaOne'));
function onSectionClick(secID){
var x = $(secID).hasClass('toggled') ? 'false' : 'true';
console.log(x)
}
Then the function is called as soon as the page is loaded, rather then when the area is clicked. I want it to work this way as there are many areas that should trigger the same function.
I'm pretty new to Javascript so any help, or advice on how to do this in a better way would be greatly appreciated.
The problem is that you call the function instead of giving a function reference. Try:
$('#areaOne').on('show.bs.collapse', function(){onSectionClick('#areaOne')});
THe line
$('#areaOne').on('show.bs.collapse', onSectionClick('#areaOne'));
calls onSectionClick, passing in '#areaOne', and passes its return avlue into on, exactly the way foo(bar()) calls bar and passes its return value into foo.
If you want to set it up to be called by the event, you pass a function reference rather than calling the function, e.g.:
$('#areaOne').on('show.bs.collapse', function() { onSectionClick('#areaOne'); });
In your case, though, you probably don't even want to do that. Instead:
$('#areaOne').on('show.bs.collapse', onSectionClick);
// Note no () -------------------------------------^
...and in onSectionClick, use this.id to get the ID of the clicked element ('areaOne' — add the # if you need it).
And if you have other "areas" that you also want to hook up, you can hook them up all at once:
$('selector-for-all-the-areas').on('show.bs.collapse', onSectionClick);
...and then you know which one relates to the event by which element this refers to in onSectionClick.
Related
EDIT: I've made a few changes to re-clarify the issue I'm having.
I have a simple Javascript plugin that I want to add some callback features to as part of its default functionality. Users would be able to specify a function to be called when a certain button is clicked on.
// Define option defaults
var defaults = {
//some other defaults
onOK: null, //callback functions to be specified here (exists as string)
onClose: null //callback functions to be specified here (exists as string)
}
Normally, this works fine when there's a simple function with no arguments. The option is passed to here:
function initializeEvents() {
//If a close button event is clicked, run this.
if (this.closeButton) {
this.closeButton.addEventListener('click', this.close.bind(this)); //Bind close event to the button
this.closeButton.addEventListener('click', window[this.options.onClose]); //passed to here
}
//If an OK button event is clicked, run this.
if (this.okButton) {
this.okButton.addEventListener('click', this.close.bind(this));
this.okButton.addEventListener('click', window[this.options.onOK]); //passed to here
}
}
window[] is supposed to become a function call to the appropriate button. It has come to my attention that I should NOT be using window[].
EDIT: However, as #adeneo pointed out, I'm making a BIG mistake here by adding my functions as strings, which simply results in the function becoming undefined instead when I have to add it to an event listener.
I've tried using an anonymous function, but I'm still not getting the function to work:
var callbackModal = new raModal({
content: '<div>You clicked on the Callback button. This shows the popup, but has a custom callback function, called when the user clicks OK and/or Close.</div>',
onOK:{function(){
okCallback('4');
}
},
onClose: "closeCallback",
closeButtonText: "Close"
});
callbackModal.open();
So, my question has changed to:
How can I properly add my anonymous functions from the defaults?
Link to a JSFiddle that reproduces the issue. You'll notice that while the Close button works as intended (with closeCallback printing the console.log message), the OK button calling okCallback(num) does nothing.
Any help is greatly appreciated.
You can pass arguments to JavaScript functions without having a parameter name on the function's signature. This should be a workaround for your problem.
See Is it possible to get all arguments of a function as single object inside that function?
Instead of trying to bind the callbacks to the button in initializeEvents() using the risky window[] implementation, it's much easier to simply append them after the button object after they're constructed.
I made two additional functions to add the event listeners for the OK and Close buttons:
function closeCallback(callback){
$(".ra-close").click(callback);
}
function okCallback(callback){
$(".ra-ok").click(callback);
}
And inside the buildOut() function, after everything is done, add the calls to the event listeners, using our custom option as the argument, which can be an anonymous function now!
closeCallback(this.options.onClose);
okCallback(this.options.onOK);
There was an additional utility function that extended user options to the new modal, but didn't track null values properly. I removed an if statement that checked for those, seeing as we had them as part of the defaults variable.
// Utility method to extend defaults with user options
function extendDefaults(source, properties) {
var property;
for (property in properties) {
source[property] = properties[property];
}
return source;
}
Here's the updated JSFiddle with the corrected plugin code.
I have a global function defined in one place:
function addToCart (){
var prodText = $(this).parent().siblings(".item1").text();
$("#"+prodId+"shopC").children(".item1").text(prodText);
alert(prodText);
}
Then, I want to call it inside a HTML element with an inline onClick event:
onClick='addToCart()'
It is not working, but it works if I put the function code directly inside the onClick event, so that must be a this scope issue.
There are many questions/explanations about this scope but I must confess I miss a simple straight answer for this specific case (I tried to use "use strict" with success either).
How to make this work?
As per current implementation this doesn't refers to the element which invoked the function. It refers to window object.
You need to pass the current element context i.e. this to the function as
onClick='addToCart(this)'
and modify the function to accept element as parameter.
function addToCart (elem){
var prodText = $(elem).parent().siblings(".item1").text();
$("#"+prodId+"shopC").children(".item1").text(prodText);
alert(prodText);
}
Basically this inside a plain function will point to window. you have to pass the this context to the inline handler onClick='addToCart(this)'. Receive it and use it inside of event handler like below.
function addToCart (_this){
var prodText = $(_this).parent().siblings(".item1").text();
you have to pass this keyword where you inline calling the addToCart function then you can capture that element
onClick='addToCart(this)'
this object in your function does not point to the object where you added the onClick function to. It rather points to the window object.
You need to pass this as a param to your function.
function addToCart (trigger) { // Code goes here }
and call addToCart(this) in your onClick.
Learn more about this in javascript here.
Sorry if my question seems naive (a bit of a newbie here), but I seem not to be able to get a simple answer to my question.
In JavaScript I try something like this
window.onload = init; *// ok, this assigns to the window load event a function that doesn't execute straight away*
// now I define my init()function
function init(){
// say...
alert('Noise!');
/* but here is my dillema...
Next say I want to assign to an button.onclick event a function that only executes when I click the button.
But (!here it is...!) I want to pass arguments to this function from here without causing to execute because it (obviously) has brackets.
Something like: */
var button = document.getElementById('button');
var promptInput = prompt("Name?");
button.onclick = printName(promtInput); // I only want to print name on button click
}
function printName(name){
alert(name);
}
So... OK, I know the example is stupid. Simply moving all prompt gathering inside printName() function and assign to button.onclick a bracketless printName function will solve the problem. I know. But, still. Is there a way to pass args to functions you don't want to execute immediately? Or it really is a rule (or 'by definition') you only use functions that await execution when you don't plan to pass args via it?
Or how do you best to this thing otherwise?
Thanks a lot!
Ty
button.onclick = function() {
printName(promptInput);
};
You can use Function.prototype.bind():
The bind() method creates a new function that, when called, has its
this keyword set to the provided value, with a given sequence of
arguments preceding any provided when the new function is called.
For example:
button.onclick = printName.bind(null, promtInput);
You could put the data that you would normally pass as an argument into some other holding location. You can either put it in a hidden HTML element, or you can use the sessionStorage API to keep it. So your code would look like this:
var button = document.getElementById('button');
var promptInput = prompt("Name?");
sessionStorage.setItem('MyName', promptInput);
button.onclick = printName; // I only want to print name on button click
}
function printName(){
alert(sessionStorage.getItem('MyName');
}
I have been coding in JS for a while now, but somehow I always get a bit of a 'scratch head' type of problem. I think that I need to do some digging in, but in the meanwhile can someone help with this one?
So here is the problem:
I am passing through the item (got by $(this)) into the callback function. This code does not work - when I really think it should. Since I have placed the $(this) into a variable (cloning the data?) and then passed it into the callback through a function, surely it should not loose the data? But it does, and horribly
// Allow hrefs with the class 'ajax' and a rel attribute set with a selector to load via ajax into that selector.
$(".ajax").unbind("click").click
(
function(e)
{
var locationhint = $(this).attr("rel");
var $location = $(locationhint);
$location.html ("<img src='images/blockloading.gif' />");
$location.load($(this).attr("href").replace("index.php", "ajax.php"), '', function($location){dready($location);});
e.preventDefault();
}
);
Now that doesn't work.
This one works:
$(".ajax").unbind("click").click
(
function(e)
{
$("#myspecificdiv").load($(this).attr("href").replace("index.php", "ajax.php"), '', function(){dready($("#myspecificdiv"));});
e.preventDefault();
}
);
I take it it's a scope problem, but I have also done this, which should work because it's exactly the same as the 'static' one above, essentially, because it's passing the text ID of the element. This one also breaks:.
$(".ajax").unbind("click").click
(
function(e)
{
var locationhint = $(this).attr("rel");
var $location = $(locationhint);
$location.html ("<img src='images/blockloading.gif' />");
var locationid = "#" + $location.attr("id");
$location.load($(this).attr("href").replace("index.php", "ajax.php"), '', function(locationid){dready($(locationid));});
e.preventDefault();
}
);
The broken ones, when I console.log locationid, return the internal HTML of the target DIV. $location.attr("id"); can't return raw HTML when there is no call to it anywhere else surely? So it's scope I take it? But then how is it getting the internal HTML to spit out?
Any solutions?
Update:
12 seconds after I posted this it occurred to me that function($location){dready($location);} the internal function on the callback might automagically pass the AJAX call response through? But why?
You cannot do this function($location){dready($location);} but you do not need to since the outer scope has $location defined the callback func will understand this var without trying to pass it into the callback funcion so in this instance
function(){dready($location);}
will work.
Another way to conquor scoping issues is to use $.proxy. This allows you to set the this context under which the function will be called. jQuery.proxy( dready, $location ) returns a function that can be used as a callback which when invoked will call the method dready with this set to to $location.
e.g
$location.load($(this).attr("href").replace("index.php", "ajax.php"), '', jQuery.proxy( dready, $location );});
Is there anyone who knows how to destroy a javascript (jquery) function?
I'm using jquery "selectable" and a function call "edit" is fired on selectable "stop" event.
Inside this "edit" function I have nested switch functions with a lot of "click" events
and I have many functions within each "click" event. My problem is,
every time I fire the "selectable" functions and events inside the function "edit" is fired again but the previous functions and events still exist.
What i do now is to unbind every event in the function "edit" on selectable "start" even.
Is this a memory leak problem?
and is there a way to "destroy" functions in javascript?
i have tried to declare the function to null when the function ends but this does not work. functions and events inside it still exist.
anyone have a clue?
demo page here -->
http://dreamerscorp.com/test/test01/javascript_destory_test.html
edit 2009/10/31
:) thanks a lot for your helps, your comments are very useful to me, thanks again!!!
You can try to nullify the function, or override it assigning an anonymous function that does nothing:
myFunction = null;
// or
myFunction = function () {};
You can also do it within the function itself:
var autoDestroy = function () {
autoDestroy = null;
//...
return 1;
};
autoDestroy(); // returns 1
autoDestroy(); // TypeError: autoDestroy is not a function
to unbind events in jquery use unbind function http://docs.jquery.com/Events/unbind
$("something").click(function() {
$("anotherOne").click(function() {
....
});
});
in this example, every time "something" is clicked, an event handler is added to "anotherOne", so if you click three times, you'll get three event handlers.
$("something").click(function() {
$("anotherOne").unbind('click').click(function() {
....
});
});
here you're guaranteed to have only one click handler on "anotherOne".
There's no need to destroy previous handler explicitly.
Basically you need to remove all references to those functions so that the JavaScript garbage collector can collect them. If they are bound, you need to unbind them. If there are other variables in there that point to them, they need to be set to null.
It might help if you posted some code; then we can give a better answer.
...EDIT:
What's happening here is, you're creating a closure that will outlive the containing function:
function edit(){
$('.edit').click(function(){
//...
function _edit(boxTitle,selectedItemAmount){
//...
$('#box .yes').click(function(){
alert(boxTitle + ' for ' + selectedItemAmount + ' selected item');
$('#msg').hide(); // hide msg box when yes btn is clicked
});
}
//...
$('#box .no').click(function(){
$('#msg').hide();
});
});
In other words, inside a function, you're saying, "Attach this function to a DOM object," and you're doing it inline. JavaScript captures variables from outer contexts and keeps them alive while the reference to the inner context is alive.
What you need to do is to define the functions somewhere not inline and then use them:
function boxClickYes(e) {
alert(e.data.boxTitle + ' for ' + e.data.selectedItemAmount +
' selected item');
$('#msg').hide(); // hide msg box when yes btn is clicked
}
function boxClickNo(e) {
$('#msg').hide();
}
function edit(){
$('.edit').click(function(){
//...
function _edit(boxTitle,selectedItemAmount){
//...
$('#box .yes').bind("click", {boxTitle: boxTitle,
selectedItemAmount: selectedItemAmount}, boxClickYes);
}
//...
$('#box .no').click(boxClickNo);
});
This also demonstrates how to use the data property in jQuery click handlers to store data in between the time you attach the handler and the time you use it (instead of storing that data in a closure that will keep the scope chain in memory). Using inline-defined functions is fine when you're just using it right there (like the body of a $.each, for instance) but it's not OK when you're attaching event handlers.
To "destroy" a function in javascript, simply ensure that the function becomes unreachable. This will enable the function to be eligible for reclamation. One thing to watch out for is that javascript variables are bound based on scopes (not as individual variables) and a scope, with many unused objects, may persist if a binding is kept to the scope: to provide any more help requires knowledge of the specific code and how it is used.
Please see the javascript delete operator as one way of removing a variable or object member. Setting the value to null/undefined/other-object removes on method of reaching the object previously referenced (although it might still be reachable otherwise and thus not be reclaimed) but does not get rid of the variable/member.
delete variable
delete obj.member
delete obj[member]