Send runtime parameter value to the jQuery's ON method? - javascript

I have a simple button. and I need to send data to the handler when clicked.
So I have this code using the ON overload method :
.on( events [, data ], handler(eventObject) )
I've created this sample :
var aaa="john";
function greet(event) { alert("Hello "+event.data.name); }
$("button").on("click", { name: aaa}, greet);
setTimeout(function (){aaa="paul"},2000)
But after waiting 2 sec , I still see: "Hello John"
So I assume that the aaa value is bounded at interpretation time.
Question :
How can I change the code so that after 2 seconds it will alert : Hello Paul ?
JSBIN

A simple way is to use an object literal. You can pass it as event data and keep a reference that you modify later:
var greetData = {
name: "john"
};
function greet(event)
{
alert("Hello " + event.data.name);
}
$("button").on("click", greetData, greet);
setTimeout(function() {
greetData.name = "paul";
}, 2000);
You will find an updated JS Bin here.

Frédéric Hamidi's answer is better. Will leave this just as an alternative.
If you change it so that you pass a function that loads the name instead of the name it self it will work:
var aaa="john";
function getName() {
return aaa;
}
function greet(event) { alert("Hello "+event.data.name()); }
$("button").on("click", { name: getName}, greet);
setTimeout(function (){aaa="paul";},2000);
http://jsbin.com/ohAJihi/4/edit

It is a little confusing to understand what are you
trying to achieve. If you are using setTimeout; theres no need to use a button onclick handler.
Secondly, the scope of the setTimeout function is always the window object. So if you need to access the value from your object, please create a reference of it and use in the setTimeout function.

Related

Javascript using bind without overwriting this

Take this example that uses a "widgetManager" object to bind events to all accordions:
widgetManager = {
name : 'widgetManager',
initiate : function(){
$('.accordion').accordion({
onClosing : this.onCloseAccordion.bind(this.name),
})
},
onCloseAccordion : function(){
console.log(name); //I want the event to receive the manager name
console.log(this); //But still be able to know which accordion was clicked
}
}
widgetManager.initiate();
If I bind something to the accordion's onClosing event, it will lose to reference to itself (the accordion that is closing), but I also need a way to be able to pass the 'name' property to the function.
Maybe bind isn't what I'm looking for, but is there a simple way to solve this?
I guess a better wording is, how to pass an object to a function without overwriting the function's scope's this
I'm using Semantic UI's accordions if that helps or changes anything, but the event has no parameters https://semantic-ui.com/modules/accordion.html#/settings
You can simply refer to widgetManager.name to get the name.
widgetManager = {
name : 'widgetManager',
initiate : function(){
var theManager = this;
$('.accordion').accordion({
onClosing : this.onCloseAccordion.bind(this),
})
},
onClosing : function(){
console.log(widgetManager.name); //I want the event to receive the manager name
console.log(this); //But still be able to know which accordion was clicked
}
}
widgetManager.initiate();
If you want something more general, you should be using a constructor function to create different managers.
function widgetManager(name) {
this.name = name;
this.initiate = function() {
$('.accordion').accordion({
onClosing: this.onCloseAccordion.bind(this);
});
return this; // For fluent interface
};
this.onCloseAccordion = function() {
console.log(name);
console.log(this);
};
};
Then you use it like this:
var theWidgetManager = new widgetManager("widgetManager");
theWidgetManager.initiate();

JavaScript general object throwing bad value

I would like to ask about some thing because I'm confused. I have this object:
var BonusesObject = {
priceTotal: 0,
bonusCheckboxClick: function(){
var price = 5;
this.priceTotal = price;
console.log("works, " + this.priceTotal);
},
getPrice: function(){
return this.priceTotal;
},
init: function(){
$('input:checkbox').on('click', this.bonusCheckboxClick);
}
};
BonusesObject.init();
//now when I will fire on click event and BonusesObject.getPrice
it will give me 0 instead of 5.
And now when I call BonusesObject.getPrice it will give me 0, as it should. Now when I manualy call BonusesObject.bonusCheckboxClick and BonusesObject.getPrice it will give ma 5, as it should. BUT when function bonusCheckboxClick will be called through on('click', this.bonusCheckboxClick) it will throw in console corret value 5 BUT after then when I will call BonusesObject.getPrice it still give me 0.
I would like to ask about some thing because I'm confused. I have this object:
Did I miss something? It only doesn't work when object function is fire by on click event.
Could someone explain me that ? I will appreciate that.
You need to bind the function to the object:
.on('click', this.bonusCheckboxClick.bind(this));
Documentation: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

need help understanding closures usage in this code

Here is a simplified snippet from some code I wrote for managing tablet gestures on canvas elements
first a function that accepts an element and a dictionary of callbacks and register the events plus adding other features like 'hold' gestures:
function registerStageGestures(stage, callbacks, recieverArg) {
stage.inhold = false;
stage.timer = null;
var touchduration = 1000;
var reciever = recieverArg || window;
stage.onLongTouch = function(e) {
if (stage.timer) clearTimeout(stage.timer);
stage.inhold = true;
if (callbacks.touchholdstart) callbacks.touchholdstart.call(reciever, e);
};
stage.getContent().addEventListener('touchstart', function(e) {
e.preventDefault();
calcTouchEventData(e);
stage.timer = setTimeout(function() {
stage.onLongTouch(e);
}, touchduration);
if (callbacks.touchstart) callbacks.touchholdstart.call(reciever, e);
});
stage.getContent().addEventListener('touchmove', function(e) {
e.preventDefault();
if (stage.timer) clearTimeout(stage.timer);
if (stage.inhold) {
if (callbacks.touchholdmove) callbacks.touchholdmove.call(reciever, e);
} else {
if (callbacks.touchmove) callbacks.touchmove.call(reciever, e);
}
});
stage.getContent().addEventListener('touchend', function(e) {
e.preventDefault();
if (stage.timer) clearTimeout(stage.timer);
if (stage.inhold) {
if (callbacks.touchholdend) callbacks.touchholdend.call(reciever, e);
} else {
if (callbacks.touchend) callbacks.touchend.call(reciever, e);
}
stage.inhold = false;
});
}
later I call registerStageGestures on a few elements (represented by 'View' objects) in the same page. Something like:
function View() {
var self=this;
..
function InitView() {
...
registerStageGestures(kineticStage, {
touchstart: function(e) {
// do something
},
touchmove: function(e) {
// do something
},
touchendunction(e) {
// do something
},
touchholdstart: function(e) {
// do something
},
touchholdmove: function(e) {
// do something
},
touchholdend: function(e) {
// do something
},
}, self);
Everything works fine, however I'm left wondering about two things in the implementation of registerStageGestures:
First, is it necessary to make inhold, timer and onLongTouch members of the stage ? or will closures make everything works well if they are local vars in registerStageGestures ?
Second, is it necessary to call the callbacks with '.call(receiver,' syntax ? I'm doing this to make sure the callback code will run in the context of the View but I'm not sure if it's needed ?
any input is much appreciated
Thanks!
First, is it necessary to make inhold, timer and onLongTouch members
of the stage ? or will closures make everything works well if they are
local vars in registerStageGestures ?
As far as registerStageGestures() is concerned, var inhold, var timer and function onLongTouch(e) {...}. would suffice. The mechanism by which an inner function has automatic access to its outer function's members is known as "closure". You would only need to set stage.inhold, stage.timer and stage.onLongTouch if some other piece of code needs access to these settings as properties of stage.
Second, is it necessary to call the callbacks with '.call(receiver,'
syntax ? I'm doing this to make sure the callback code will run in the
context of the View but I'm not sure if it's needed ?
Possibly, depending on how those callbacks are written. .call() and .apply() are sometimes used when calling functions that use this internally. In both cases, the first parameter passed defines the object to be interpreted as this. Thus, javascript gives you the means of defining general purpose methods with no a priori assumption about the object to which those methods will apply when called. Similarly, you can call a method of an object in such a way that it acts on another object.
EDIT:
For completeness, please note that even in the absence of this in a function, .apply() can be very useful as it allows multiple parameters to be specified as elements of a single array, eg the ubiquitous jQuery.when.apply(null, arrayOfPromises)...
There are some simple answers, here.
First, closure:
Closure basically says that whatever is defined inside of a function, has access to the rest of that function's contents.
And all of those contents are guaranteed to stay alive (out of the trash), until there are no more objects left, which ere created inside.
A simple test:
var testClosure = function () {
var name = "Bob",
recallName = function () { return name; };
return { getName : recallName };
};
var test = testClosure();
console.log(test.getName()); // Bob
So anything that was created inside can be accessed by any function which was also created inside (or created inside of a function created in a function[, ...], inside).
var closure_2x = function () {
var name = "Bob",
innerScope = function () {
console.log(name);
return function () {
console.log("Still " + name);
}
};
return innerScope;
};
var inner_func = closure_2x();
var even_deeper = inner_func(); // "Bob"
even_deeper(); // "Still Bob"
This applies not only to variables/objects/functions created inside, but also to function arguments passed inside.
The arguments have no access to the inner-workings(unless passed to methods/callbacks), but the inner-workings will remember the arguments.
So as long as your functions are being created in the same scope as your values (or a child-scope), there's access.
.call is trickier.
You know what it does (replaces this inside of the function with the object you pass it)...
...but why and when, in this case are harder.
var Person = function (name, age) {
this.age = age;
this.getAge = function () {
return this.age;
};
};
var bob = new Person("Bob", 32);
This looks pretty normal.
Honestly, this could look a lot like Java or C# with a couple of tweaks.
bob.getAge(); // 32
Works like Java or C#, too.
doSomething.then(bob.getAge);
? Buh ?
We've now passed Bob's method into a function, as a function, all by itself.
var doug = { age : 28 };
doug.getAge = bob.getAge;
Now we've given doug a reference to directly use bobs methid -- not a copy, but a pointer to the actual method.
doug.getAge(); // 28
Well, that's odd.
What about what came out of passing it in as a callback?
var test = bob.getAge;
test(); // undefined
The reason for this, is, as you said, about context...
But the specific reason is because this inside of a function in JS isn't pre-compiled, or stored...
this is worked out on the fly, every time the function is called.
If you call
obj.method();
this === obj;
If you call
a.b.c.d();
this === a.b.c;
If you call
var test = bob.getAge;
test();
...?
this is equal to window.
In "strict mode" this doesn't happen (you get errors really quickly).
test.call(bob); //32
Balance restored!
Mostly...
There are still a few catches.
var outerScope = function () {
console.log(this.age);
var inner = function () {
console.log("Still " + this.age);
};
inner();
};
outerScope.call(bob);
// "32"
// "Still undefined"
This makes sense, when you think about it...
We know that if a function figures out this at the moment it's called -- scope has nothing to do with it...
...and we didn't add inner to an object...
this.inner = inner;
this.inner();
would have worked just fine (but now you just messed with an external object)...
So inner saw this as window.
The solution would either be to use .call, or .apply, or to use function-scoping and/or closure
var person = this,
inner = function () { console.log(person.age); };
The rabbit hole goes deeper, but my phone is dying...

How to detect when an .html() function is called in jQuery?

The problem is simple. I have a massive javascript application. And there are lot of times in the app where I use code which looks something like this -
$('#treat').html(new_data);
....
....
$('#cool').html(some_html_data);
....
....
$('#not_cool').html(ajax_data);
So what I want to do is, everytime this html() function is called I want to execute a set of functions.
function do_these_things_every_time_data_is_loaded_into_a_div()
{
$('select').customSelect();
$('input').changeStyle();
etc.
}
How do I do this? Thank you.
You can use the custom event handlers for that:
$('#treat').html(new_data);
// Trigger the custom event after html change
$('#treat').trigger('custom');
// Custom event handler
$('#treat').on('custom', function( event) {
// do_these_things_every_time_data_is_loaded_into_a_div
alert('Html had changed!');
});
UPDATE
Based on answer over here and with some modifications you can do this:
// create a reference to the old `.html()` function
$.fn.htmlOriginal = $.fn.html;
// redefine the `.html()` function to accept a callback
$.fn.html = function (html, callback) {
// run the old `.html()` function with the first parameter
this.htmlOriginal(html);
// run the callback (if it is defined)
if (typeof callback == "function") {
callback();
}
}
$("#treat").html(new_data, function () {
do_these_things_every_time_data_is_loaded_into_a_div();
});
$("#cool").html(new_data, function () {
do_these_things_every_time_data_is_loaded_into_a_div();
});
Easily maintainable and less code as per your requirements.
You can overwrite the jQuery.fn.html() method, as described in Override jQuery functions
For example, use this:
var oHtml = jQuery.fn.html;
jQuery.fn.html = function(value) {
if(typeof value !== "undefined")
{
jQuery('select').customSelect();
jQuery('input').changeStyle();
}
// Now go back to jQuery's original html()
return oHtml.apply(this, value);
};
When html() is called it usually make the DOM object changes, so you can look for DOM change event handler, it is called whenever your HTML of main page change. I found
Is there a JavaScript/jQuery DOM change listener?
if this help your cause.
You can replace the html function with your own function and then call the function html:
$.fn.html = (function(oldHtml) {
var _oldHtml = oldHtml;
return function(param) {
// your code
alert(param);
return _oldHtml.apply(this, [param]);
};
})($.fn.html);
I have a little script for you. Insert that into your javascript:
//#Author Karl-André Gagnon
$.hook = function(){
$.each(arguments, function(){
var fn = this
if(!$.fn['hooked'+fn]){
$.fn['hooked'+fn] = $.fn[fn];
$.fn[fn] = function(){
var r = $.fn['hooked'+fn].apply(this, arguments);
$(this).trigger(fn, arguments);
return r
}
}
})
}
This allow you to "hook" jQuery function and trigger an event when you call it.
Here how you use it, you first bind the function you want to trigger. In your case, it will be .html():
$.hook('html');
Then you add an event listener with .on. It there is no dynamicly added element, you can use direct binding, else, delegated evets work :
$(document).on('html', '#threat, #cool, #not_cool',function(){
alert('B');
})
The function will launch everytime #threat, #cool or #not_cool are calling .html.
The $.hook plugin is not fully texted, some bug may be here but for your HTML, it work.
Example : http://jsfiddle.net/5svVQ/

Javascript - Add Variables in Jquery Command

function bookRemoved(bookId)
{
alert(bookId) ;
$(document).ready(function() {
$('.className').hide(1000); // Want it Here
} );}
In Above example, the bookId returns class Name. How can I use it as class name to hide it as shown in above example.
I would like something like this:
$('.bookId').hide(1000); // Want it Here
bookId should return its value. Suppose if bookId has 23, it should look like this.
$('.23').hide(1000); // Want it Here
PS: I am new to Javascript and Jquery
Try like
function bookRemoved(bookId)
{
alert(bookId) ;
$('.' + bookId).hide(1000); // Want it Here
}
We dont need DOM ready there,in the defined function.
What you are looking for is a means of passing the argument of one function as argument to another. Variables can be passed into functions, $ is just a function, within the (...) sequence following a function reference.
function bookRemoved(bookId) {
alert(bookId);
$(document).ready(function() {
$('.'+bookId).hide(1000);
});
};
In this case:
the function alert is fired on the variable bookId
the function $ is fired on the document variable.
the method ready is accessed from the returned object of $(document)
ready is fired with a function, an anonymous function (i.e., without a name)
the function $ is fired on a string concatenation between . and bookId
the hide method is then accessed and fired with 1000
Hopefully this better explains the general terminology and what it is that is occurring within the function definition.
function bookRemoved(bookId) {
alert(bookId);
$(document).ready(function () {
$('.' + bookId).hide(1000);
});
}
Do you mean this?
$('.' + bookId).hide(1000);
In javascript + operator is used to...
add nembers.
concatenate strings if one of the operand is string.
for example...
1 + 2 == 3
"abc" + "xyz" == "abcxyz" and
"1" + 2 == "12" // "1" here is a string
Cleaner approach. Seperate your document.ready function.
$(document).ready(function() {
function bookRemoved(bookId) {
$(bookId).hide(1000);
}
//pass in your specified class name OR id as the argument.
bookRemoved('.someClasssName');
});
More info on hide method here: http://api.jquery.com/hide/

Categories

Resources