Passing function variables into a second function changes variable values - javascript

I imagine this is just a Javascript-oriented feature that I'm not aware of, but coming from a C# back-end environment means I assume a lot of JS's functionality works the same, which isn't usually the case.
With the below code, I am trying to pass the values of field and section further down into a second function defined on rows.each, however when I check in the debugged the field and section variables being used in the line if (classList.contains(field) && classList.contains(section)) have different values that what I expect them to have.
They are obviously being changed somewhere, but I am unsure for what reason.
function setValidationRowToFailure(field, section) {
var rows = $("#PeekInstruction .validation-row");
rows.each(function(field, section) {
var classList = $(this).find(".validation-row-value").attr("id");
if (classList.contains(field) && classList.contains(section)) {
$(this).removeClass("validation-row-success");
$(this).addClass("validation-row-failure");
}
})
}
To further support my point that the original values of field and section aren't being used, they are actually grey in my IDE. This is definitely a scope issue.

Your problem seems to be, that the arguments defined here:
function setValidationRowToFailure(field, section) {
are being shadowed by these ones:
rows.each(function(field, section) {
You should be fine by removing the arguments inside the function passed to .each:
rows.each(function() {
Or just rename them:
rows.each(function(index, element) {
Note, that jQuery's .each passes an index variable as the first argument: https://api.jquery.com/each/#each-function

Related

jQuery.each not running in function

I'm working on a project where some form elements depend on another form input to have a certain value, or possibly multiple elements with specific values before that input is shown.
The idea is that when the form is generated, the wrapping div for each input has a data-depends-on attribute with a comma-separated list of each field that it depends on to be shown, and the values for each that it's expecting to be shown.
I almost have the front-end / JavaScript code down to do the lifting, but for some reason my jQuery.each() loop in a JavaScript function isn't running even though I've confirmed the array I'm trying to loop through a. has content, and b. that the functioning is actually being called when it is expected to do so.
First, I have the actual function call (which is called whenever a dependency input is changed):
checkShowField(keyed_depends, current_vals, targeted_element);
And then the function checkShowField() definition:
function checkShowField(keyed_dependencies, current_values, targeted_element)
{
var hide_field = null;
jQuery.each(keyed_dependencies, function(key, value)
{
if (value != current_values[key] && hide_field == null)
hide_field = false;
});
if (hide_field == null)
$(targeted_element).slideDown();
else
$(targeted_element).slideUp();
}
Also please note that the function call is placed in the proper place, and is actually being called. I just added the code on here to show everyone context of how the function is being called. The function call is wrapped in $(document).ready(function() {...}.
So as you can see, in the function "checkShowField", I have a jQuery.each loop that should be looping through keyed_dependencies array, but in actuality, the loop isn't even running once. Thoughts?
You can check, if keyed_dependencies in argument list has a property length. If so, jQuery assumes an array and might actually fail to run you loop.
If that is the case, try using vanilla JS:
for (var key in keyed_dependencies) {...}
Hope that helps.
It looks like the keyed_dependencies is not really what you think it is. Try adding debugger; statements before the .each line and maybe in the function as well. Then use inspector/debugger to review the data in the variables.

Passing code as function argument in Javascript and that code uses variable defined in the function

I have following javascript code. I am trying to achieve a hide/display generic function on change in select form field. Lets take an example. Suppose I have a select field where values are "none", "auth", "other" and if user selects "auth" then display another form field. I may have such situation many places in the form for different select fields. Hence I have written this function below profile_field_toggler
function escapeJquerySelectorStr(str) {
if (str) {
return str.replace(/([ #;?&,.+*~\':"!^$[\]()=>|\/#])/g,'\\$1');
}
return str;
}
function profile_field_toggler (cotroller_id, reciever_id, block_code) {
parsed_controller_id = "#"+escapeJquerySelectorStr(cotroller_id);
parsed_reciever_id = "#"+escapeJquerySelectorStr(reciever_id);
selected = jQuery(parsed_controller_id + " option:selected").val();
if(block_code()) {
jQuery(parsed_reciever_id).removeClass("hide");
} else {
jQuery(parsed_reciever_id).addClass("hide");
}
}
Here I make call to the generic function i.e profile_field_toggler. Last argument of the generic function accepts conditional code, so I am passing it in the call below selected == "auth" . BUT selected has no scope in this call hence it will not work (as I want to refer the selected variable in the generic function). So how can I solve this problem? Please note, I must want to keep the condition such as selected == "auth" or selected != "xyz" or selected == "undefined" in the caller only, because they may vary.
jQuery(document).ready(function() {
profile_field_toggler("base[remove_option]", "general_passwordassword_block", function() {selected == "auth"});
});
Any idea how to solve this problem?
A closure can only access variables in its own scope, but definitely will not (and can not) have any knowledge of the scope of the function that eventually calls it.
In this particular example - perhaps "block_code" should take selected as an argument.
It looks weird, but I found the solution. It seems you can pass selected in the call from caller only and even if it is defined in the generic function, it will work. Check this out. I just added return so that condition can be evaluated. and it worked. I tested with all different conditions and it is working ..sweet, but weird.
jQuery(document).ready(function() {
profile_field_toggler("base[remove_option]", "general_passwordassword_block", function() {return(selected == "auth")});
});

javascript / jquery - get and hold element's initial html content

This is probably very basic but I'm stalling ...
On page load, I need to save the html content of my element into a variable. I have other code in the page that will change the html content of the element. So I need to be able to revert the value back to it's default (what it was on page load). The issue is that my variable's value is being changed to most recent value.
How can I make the initial value I assign to the variable "stick"?
currentElementsHTML = $("#myDOMElement"),
currentElementsHTMLDefaultValue = currentElementsHTML.html()
... do stuff that changes currentElementsHTML
... revert to currentElementsHTMLDefaultValue whenever i need to
There are many ways you can store some data and make it available later, some of these require a knowledge of the way JavaScript's scope works - others just rely on jQuery methods.
the first things that come to mind
global variable
The bad way to do this would be to store the value as a global var:
function at_the_start(){
/// notice there is no var keyword, this means the variable will be global
global_html = $('element').html();
}
function later_on(){
$('element').html( global_html );
}
You shouldn't do this because your data will "pollute the global namespace" - which basically means that other code will easily be able to access your variable (and mess around with it) and that you could inadvertantly overwrite some other code's global data - especially if you use a rather general variable name.
local variable kept in scope
A better way to do this would be to use the power of JavaScript for your own ends, namely its scope abilities, there are some good points to read here -- What is the scope of variables in JavaScript?:
function my_code(){
var html = $('element').html();
/* Do stuff here */
$('element').html( html );
}
The above relies on a local variable and the fact that you must keep everything in the same function call. As it is most likely you will be relying on a mixture of user triggered events, you can't really use the above. This is because you will have many functions used in different locations and they can't all share the same local variable. Or can they?
The following is what I call a "global local" variable - completely most likely not its real name, but it describes things as I see them:
function my_code(){
/// this variable is local, due to the var keyword
/// but it will be accessible in both the functions below
var html_local = '';
var my_function_to_start = function(){
html_local = $('element').html();
}
var after_other_things_have_happened = function(){
$('element').html( html_local );
}
/// you can even apply these functions to say an event handler
/// and the variable will be remembered because it exists within
/// the "after_other_things_have_happened" function's scope.
$('another.element').click(after_other_things_have_happened);
}
The above works because JavaScript functions can always access variables defined in previous parent blocks / parent scopes or parent functions.
jQuery data
Considering you are using jQuery, jQuery offers a very simple method for storing arbitrary data and you don't need to know anything about scope or local and global vars. It's taken me a while to write this and so obviously by this time other posters have correctly stated that the following is a good idea - jQuery Data:
$('element').data( 'old_html', $('element').html() );
This can then be accessed any time after by using:
$('element').data( 'old_html' );
So...
$('element').html( $('element').data( 'old_html' ) );
Will put the value back - this is stored along with the element so whereever you can access $('element') you'll be able to get at the data assigned to it.
Some other less relevant ways (but still methods of data storage)
storing as a property of an object
Another useful ability sometimes, is that JavaScript treats nearly every datatype as an object. This means you can add properties to nearly anything. The following is actually quite possible if a little odd.
var a = new String('This is a string');
a.withAProperty = 'another string';
alert(a);
alert(a.withAProperty);
I occasionally use this to create pseudo static properties on functions, like so:
var my_function = function(){
if ( ! my_function.staticProp ) {
my_function.staticProp = 'abc';
}
/* use my_function.staticProp for something here */
}
var another_function(){
/* you can also access my_function.staticProp here
but only after my_function has been called once */
}
/* and my_function.staticProp here, but only
after my_function has been called once */
This almost has the same affect of using a global var (especially if you apply it to global functions) but means your value is stored on top of your functions namespace, cutting down the possibility of collisions with other code quite drastically. It does still mean outside code can influence the content of your var -- which can actually be a benefit depending on what you want to do.
storing content in the dom
Depending on what you wish to store, it can sometimes be of benefit to record that data in the DOM. The most obvious of these would be to write the data into a hidden input or hidden element. The benefit of the latter is that you can still navigate this data (using the likes of jQuery or document.getElementById) if it happens to take the form of markup information (as yours does). This can also be beneficial way of avoiding memory leaks caused by circular references - if you are dealing with large amounts of data - as long as you make sure to empty your variables involved in the transporting of the data.
$.ajax('request_html.php').done(function(data){
$('<div id="hidden_html" />').hide().html(data).appendTo('body');
data = null;
/// you only need mullify data if you were to have other
/// sub/child functions within this callback, mainly being wary
/// of closures - which are functions that are defined in a certain
/// scope chain, but are then returned or put to use outside of
/// that chain - i.e. like event listeners.
/// nullify vars and removing large properties is still good practice though.
});
Then when you want to retrieve:
$('#hidden_html').html();
And in the meantime between those two points you can obviously still traverse the data:
$('#hidden_html h1 > a[name=first]');
You associate the original HTML with the same DOM element, that way it won't disappear:
$("#myDOMElement").data("initial-html", $("#myDomElement").html());
something like that, but not tested yet:
$(function() {
$('#id').data('store', $('#id').html());
});
...
$('#id').html(data('store'));
Set it and forget it.
If you push the contents of .html() into a variable, it will stay there unless you do something with that variable to remove it:
var original = $("#foo").html(); // original HTML is now in 'origina'
This won't change unless you change it.
Storing data on the element with $.data()
It might be more advantageous for you to store it as data (using jQuery's .data method) on the element itself though:
var element = $("#foo");
element.data( "original", element.html() );
This way you can always access it at a later time:
console.log( element.data( "original" ) );
Record, Reset, and Restore Demo: http://jsfiddle.net/ft8M9/
Works on many items too
// Access all elements to restore
var restore = $(".restore");
// Save original HTML, and set new HTML
restore.each(function(i,o){
var that = $(this);
that.data("original", that.html())
.html("Changed " + i);
});
// After 2 seconds, restore original HTML, remove stored data
setTimeout(function(){
restore.each(function(i,o){
var that = $(this);
that.html( that.data("original") )
.removeData( "original" );
});
}, 2000);
Demo: http://jsfiddle.net/ft8M9/1/

Programatically output variable and object names as literal text strings

I'd really like to track variables without switching between Firebug console windows or clicking around so much, so I want to draw a runtime viewer of variable names and their corresponding values that will display on the page of the app I am building.
I'd like to two functions, show(variableName) and freeze(variableName). They will output both the variable's value and the name of the variable or object as a literal string which will serve as the text label in the viewer. freeze(variableName) is the same as show(variableName) except with a setTimeOut timer for tracking loops.
I'm sure I'm missing something basic, but I haven't found out a way to get the string that comprises the name of a value programmatically so I can use it as a label. I guess I could create the table with hardcoded labels prior to runtime and just populate it with values at runtime, but I really want to generate the table dynamically so it only has those variables I specifically want to show or freeze. Simple functions:
foo1 = "Rock";
show(foo1);
foo2 = "Paper";
show(foo2);
foo3 = "Scissors";
show(foo3);
should output this via getElementById('viewer-table'):
<table>\<tr><td>foo1</td><td>Rock</td></tr><tr><td>foo2</td><td>Paper</td></tr><tr><td>foo3</td><td>Scissors</td></tr></table>
I've tried this solution:
How to convert variable name to string in JavaScript?
and eval() but it's not working for me...I dunno, shouldn't this be easy? Getting frustrated...
Thanks,
motorhobo
I am not sure you can actually get the "name" of the variable that is being passed into a function for two reasons:
1) The variable is just an identifier. In fact, you could have multiple identifiers reference the exact same object. You are (generally) passing that reference, not any actual object.
2) The show/freeze function is going to stomp on the identifier name, either through named arguments in the function declaration or by referencing them through the arguments array.
I was trying to think if there was some clever way to use the arguments.callee or the stack property on an exception in Firefox... but I can't see anything that would expose the arguments as you desire.
What I would recommend is to simply add the name of the variable and its value to a simple object, and call one of the various jsDump methods (I prefer the one in QUnit):
function show(o) {
document.getElementById("viewer-table").innerHTML = QUnit.jsDump(o);
}
// actually use the method
show({"foo1":foo1});
There's no easy way to solve this as the called function simply doesn't know the original name of the variable. You couldn't solve this with reflection even (esp. in javascript) so you'll have to pass the name of the variable to the function too. To follow the link you posted:
function show(varObject)
{
for(name in varObject)
{
alert(name + ": " + varObject[name]);
// save both name and reference to the variable to a local "to observe array"
}
}
And call it with
var test = "xxx";
show({'test' : test});
Within the for loop you could add easy variable to a monitor array and update your gui in fixed time intervalls (you can't be notifed when a signle variable changes it's value. You need some kind of global monitor/observer which exactly you're trying to create).

How to assign event callbacks iterating an array in javascript (jQuery)

I'm generating an unordered list through javascript (using jQuery). Each listitem must receive its own event listener for the 'click'-event. However, I'm having trouble getting the right callback attached to the right item. A (stripped) code sample might clear things up a bit:
for(class_id in classes) {
callback = function() { this.selectClass(class_id) };
li_item = jQuery('<li></li>')
.click(callback);
}
Actually, more is going on in this iteration, but I didn't think it was very relevant to the question. In any case, what's happening is that the callback function seems to be referenced rather than stored (& copied). End result? When a user clicks any of the list items, it will always execute the action for the last class_id in the classes array, as it uses the function stored in callback at that specific point.
I found dirty workarounds (such as parsing the href attribute in an enclosed a element), but I was wondering whether there is a way to achieve my goals in a 'clean' way. If my approach is horrifying, please say so, as long as you tell me why :-) Thanks!
This is a classic "you need a closure" problem. Here's how it usually plays out.
Iterate over some values
Define/assign a function in that iteration that uses iterated variables
You learn that every function uses only values from the last iteration.
WTF?
Again, when you see this pattern, it should immediately make you think "closure"
Extending your example, here's how you'd put in a closure
for ( class_id in classes )
{
callback = function( cid )
{
return function()
{
$(this).selectClass( cid );
}
}( class_id );
li_item = jQuery('<li></li>').click(callback);
}
However, in this specific instance of jQuery, you shouldn't need a closure - but I have to ask about the nature of your variable classes - is that an object? Because you iterate over with a for-in loop, which suggest object. And for me it begs the question, why aren't you storing this in an array? Because if you were, your code could just be this.
jQuery('<li></li>').click(function()
{
$(this).addClass( classes.join( ' ' ) );
});
Your code:
for(class_id in classes) {
callback = function() { this.selectClass(class_id) };
li_item = jQuery('<li></li>')
.click(callback);
}
This is mostly ok, just one problem. The variable callback is global; so every time you loop, you are overwriting it. Put the var keyword in front of it to scope it locally and you should be fine.
EDIT for comments: It might not be global as you say, but it's outside the scope of the for-loop. So the variable is the same reference each time round the loop. Putting var in the loop scopes it to the loop, making a new reference each time.
This is a better cleaner way of doing what you want.
Add the class_id info onto the element using .data().
Then use .live() to add a click handler to all the new elements, this avoids having x * click functions.
for(class_id in classes) {
li_item = jQuery('<li></li>').data('class_id', class_id).addClass('someClass');
}
//setup click handler on new li's
$('li.someClass').live('click', myFunction )
function myFunction(){
//get class_id
var classId = $(this).data('class_id');
//do something
}
My javascript fu is pretty weak but as I understand it closures reference local variables on the stack (and that stack frame is passed around with the function, again, very sketchy). Your example indeed doesn't work because each function keeps a reference to the same variable. Try instead creating a different function that creates the closure i.e.:
function createClosure(class_id) {
callback = function() { this.selectClass(class_id) };
return callback;
}
and then:
for(class_id in classes) {
callback = createClosure(class_id);
li_item = jQuery('<li></li>').click(callback);
}
It's a bit of a kludge of course, there's probably better ways.
why can't you generate them all and then call something like
$(".li_class").click(function(){ this.whatever() };
EDIT:
If you need to add more classes, just create a string in your loop with all the class names and use that as your selector.
$(".li_class1, .li_class2, etc").click(function(){ this.whatever() };
Or you can attach the class_id to the .data() of those list items.
$("<li />").data("class_id", class_id).click(function(){
alert("This item has class_id "+$(this).data("class_id"));
});
Be careful, though: You're creating the callback function anew for every $("<li />") call. I'm not sure about JavaScript implementation details, but this might be memory expensive.
Instead, you could do
function listItemCallback(){
alert("This item has class_id "+$(this).data("class_id"));
}
$("<li />").data("class_id", class_id).click(listItemCallback);

Categories