Javascript function breaking/variables being forgotten - javascript

It's hard to describe the issue within the title, but basically, I have a function that, when a button is clicked, checks a bunch of attributes on the element in order to display/hide associated content.
It all works great, except when I tried to add an extra feature that hides a panel when a certain tab is selected. It's a simple if statement I thought I'd have no trouble with, but it has the strangest reaction I've ever seen.
There are 3 main tabs, and one of those tabs with a value of 'mirror' closes a group of tabs relating to patterns for a controller designer I am working on. When 'mirror' is clicked, the variable 'dependingvar' is set fine, however - it will only be used once. Here is the code of the if statement:
if (dependingvar !== undefined) {
jQuery('.' + dependingvar).children('.cdesign-toggle').first().trigger('click');
jQuery('.' + dependingvar).hide();
lastdepend = dependingvar;
} else {
jQuery('.' + lastdepend).show();
}
Only the very first line that triggers a click will run. The second one that hides the group does not run. If I swap these around, the block will hide, but the trigger wont run. I've checked the debug console, and the variable is literally getting unassigned as soon as it's ran the first one. I have no idea what is happening with it.
Here is the full code:
http://pastebin.com/6QncXf6c
I can't really do a JSFiddle due to the dependancies, but here's the HTML as a pastebin:
http://pastebin.com/nePQbtjE
I really just have no idea why this is happening. An important note, as well:
If the if statement is placed anywhere apart from the bottom, it breaks the rest of the code, unsetting variables, but ONLY if the condition is met (dependingvar !== undefined).
I'm hoping it's a stupid mistake, but can anyone spot it?

I've only quickly skimmed over your code, but a few things springs to mind:
.trigger('click') is run asynchronously. The event handler will set it to undefined (use null or something else by the way - undefined should only be used for undefined variables) .
In the very next line, you are referencing dependingvar again, but depending on whether or not the .cdesign-toggle click event has run to end, dependingvar may have one value or another. Instead of referencing dependingvar directly, try cloning it, so you know its value won't change inside your if statement, something along the lines of:
if (dependingvar !== undefined) {
var dependingvar_ = dependingvar; // or jQuery.clone(dependingvar) depending on the type of dependingvar
jQuery('.' + dependingvar_).children('.cdesign-toggle').first().trigger('click');
jQuery('.' + dependingvar_).hide();
lastdepend = dependingvar_;
} else {
jQuery('.' + lastdepend).show();
}

You defined dependingvar outside the function, so it is in global scope. Every change you make by other function-calls (in this case by calling children('.cdesign-toggle').first().trigger('click'); will also be effective within your current scope.
This code will demonstrate this: http://jsfiddle.net/petervanderwal/r87b1f9k/1/
Move the definition inside the function (commented out in the demo), so other calls to click wont alter the same dependingvar.

Related

Using "this" as an argument

I have an onclick function to navigate from one "page" to another (it's not actually navigating, just imitates it):
$('.button').on('click', function(){
$('.home').css('display','none');
var newPage = $('.'+this.id);
goTo(newPage);
});
goTo simplified for reference:
function goTo(page){
$(page).css('display', 'block');
}
This works perfectly fine. All of the navigation buttons have the class of button, and also an ID that matches the class name of the different "pages". Click #page1, display .page1, etc.
My problem is now I'm having to rewrite my code to do the same thing for other elements - trying to rewrite this with arguments doesn't work for this in particular.
Here's what I'm trying:
function goToPage(link, destination){
link.click(function(){
$('.home').css('display','none');
goTo(destination);
}
}
and calling it as:
goToPage($('#page1'), $('.page1'));
works fine, however:
goToPage($('.button'), $('.'+this.id));
doesn't.
I suppose I'm misunderstanding how "this" is working in this context. I thought it would only determine what "this" is when the argument is called.
So my question is: can "this" be used as an argument in this way, am I slightly off with the logic or am I a complete idiot?
Fiddle: https://jsfiddle.net/hek0ptca/13/
To explicitly answer your question, no, this cannot be used as an argument in this case because it points to nothing.
goToPage($('.button'), $('.'+this.id));
In this context, this points to "undefined". Try running console.log(this.id);at the same scope of the code mentioned above and check your browser's console. It returns "undefined".
A good way to think about this is that you need something for it to reference. Scope matters. If there is nothing for this to reference, you will always get "undefined" as a value. Typically this is used inside a function where an object has already been referenced, for example, inside your event handler:
$('.button').click(function(){
$('#home').css('display', 'none');
goTo($('.'+this.id));
});
This will work in this case because this will refer back to the object that is being operated on, the .button class.

What are the consequences of creating a custom own property (not a custom attribute) on an HTML element?

In an answer I gave to another SO question I was very proud of having come up with a very short click-handler-toggle. Slightly re-written, it's this:
document.getElementById("myToggle").addEventListener("click", function() {
(this.oddClk = !this.oddClk) ? oddHandler() : evenHandler();
});
This isn't central to my question, but in case you're wondering what I'm doing here, read this paragraph. I'm adding a custom property, oddClk, to the HTML element I clicked. After the first click, this property is initially undefined but then is immediately negated to true. (Note that the first part of the ternary statement intentionally uses = not == or ===.) The second time through it's negated again to false, etc. Based on its value, one of two handlers is invoked. Not only is the code short, but it also doesn't require any global variables, and can be used on multiple targets simultaneously. If you're interested, here's a working version:
document.getElementById("myToggle").addEventListener("click", function() {
(this.oddClk = !this.oddClk) ? oddHandler(): evenHandler();
});
function oddHandler() {
document.getElementById("result").innerHTML = "odd";
}
function evenHandler() {
document.getElementById("result").innerHTML = "even";
}
<div id="result">Not clicked yet</div>
<button id="myToggle">Click me to toggle</button>
My question, though, is this: What are the consequences of using this.myCustomProperty where this is an HTML element (in this case, the target that was clicked)?
Note that I'm not setting a custom attribute on the element. Rather, I'm setting a new custom property on the HTML element. It actually ends up being the only own property on the element. Are there any significant side-effects of assigning an own property onto an HTML element object like this, side-effects that might have negative consequences elsewhere?
I've found other questions on custom attributes, but that's clearly a different kettle of fish. And references to this in discussions of addEventListener don't seem to bring up new own properties.
One possible down-side is that I might pick a property name that will be defined in future versions of HTML. In that case my own property would clobber the newer inherited property. However, if I name my property carefully or strangely enough, I should be OK on that front. So, are there other problems?
Note that I'm not really asking to see if I should include this in production code. I wrote my toggle routine above basically just as a mental exercise, to see how short I could write it. I'm asking more just to better understand the inner workings of HTML.

Prototype function creating elements and using inheritance invoked on load instead of on click

I apologize if the following are stupid questions, this is my first time trying something with OO JS which goes beyond the very basic tutorials out there.
If I get to understand the following questions, it would constitute something like a person breakthrough:-)
Basically, I want to create an element- a div with a background pic- on click and append it to the button.(Later on i want to create an additional button which will replace the above pic with another one).
The pictures are stored in an Array.
If i run this with the commented out lines 37-57, it all works, but i do not want to write that function every time to create the next element.
So I have created the function object "Nation"(lines 4 to 30) and want to pass 2 arguments on call, "land"(name of nation) and "imageIndex"(index of picture in the array).
Here is where the problems start. I want to call new Nation on click(line, but it is executed straight on page load instead. How to fix that?
And I have not passed the second argument now, as I could not figure out how to do it, so I just used line 13 to set the BG pic. But the goal would be to set the BG pic by passing a second argument to the function.
var croats = new Nation("croatia");
document.getElementById("newDom")
.addEventListener("click", croats.create("croatia"));
That is the event handling and the code is here:
http://codepen.io/damianocel/pen/gPggLB
Thank you very much.
You should simply pass a function as 2nd argument of the addEventListener:
document.getElementById("newDom").addEventListener("click", function () {
croats.create("croatia")
});
See for example: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Example
As your code is currently set, it executes croats.create() when attaching the listener, instead of storing a reference to a function (called "listener" or "callback") to be executed when the event occurs.
Updated CodePen: http://codepen.io/anon/pen/KVqgdP

about javascript event handler, it works weird

I am a beginner in javascript. and have no experience in programming, at all.
So I'd like you to be generous to beginner.
And here is my question.
I'm trying to code javascript unobtrusively.
So I put in all of my js codes into external js file. for example : test.js
and deleted these codes. to do unobtrusive js coding. for example :
and I tried to use these 2 methods :
variable.onclick=test(arg1, arg2);
variable.addEventListener('click',test(arg1, arg2),true);
but these triggers didn't work.
to put it delicately, function test(arg1, arg2) worked right after dom loding finished. regardless of activating 'click' trigger.
So I spent several hours solving this problem, and finally got a solution. this is it.
variable.onclick = function(){
variable.addEventListener('click',test('arg1','arg2'),true);
}
I wanna know why first two methods didn't work, and why that solution works well.
I solved the problem, but don't know why, and how...
In JavaScript, when you reference a function by name and follow that reference by a parenthesized list of arguments, that means that you want to call the function, right then and there. Thus a statement like
variable.onclick=test(arg1, arg2);
will assign to the "onclick" property the value obtained by calling the "test" function. In other words that statement means
Please call the function "test" passing it "arg1" and "arg2", and assign whatever it returns to the "onclick" property of the object referenced by "variable".
An event handler must be a function, however, and your "test" handler probably returns either nothing, or something that's not a function. So it didn't work.
Your solution, however, is also incorrect. You're successfully assigning a function to the handler property, but your function is itself installing another event handler. There's no reason to do that here, and in general setting up event handlers from within other event handlers is a suspicious practice. All you need is:
variable.onclick = function() { test(arg1, arg2); };
variable.onclick requires a function declaration by design. In your case you could have just done
variable.onclick = function(){
test(arg1,arg2);
};
The way you did it won't work because you're not giving the click handler any instructions. The corrections I have made say that when the variable (the one with the click handler attached) is clicked trigger this function that will in turn trigger the test function.
Same thing goes for the second one
variable.addEventListener('click', function(){
test(arg1,arg2);
});
This works again because you are saying when this variable is clicked run the function that will trigger the test function.
Basically you are trying to assign the result of running a function, the test function as a task for the click handler to run. This won't work except maybe your test function returns a function that contains code that you want to run when the click event is triggered. Hope this helps.

'Strange' variable assignment behavior inside JQuery on("click") function

I'm new to both javascript and jquery, and I'm having a small problem. It is happening with both the latest versions of Chrome and Firefox, so I don't think it's a browser issue.
Relevant code is here (this is a self-invoking function inside script tags on the HTML page).
(function () {
"use strict";
var submitbutton = $('#submitcommand'),
textinput = $('#textinput'),
userinput = textinput.val();
submitbutton.on('click', function() {
alert("textinput is " + textinput.val()); // => works
alert("userinput is " + userinput); // => undefined
});
}());
The first call to alert() works just fine, using 'textinput.val()'. The second call to alert() doesn't return any visible text, because 'userinput' is evaluating to 'undefined'.
I've tried going into the Firebug Console and pasting the four statements (the two assignments and the two calls to alert()) in, one after the other. That works, and gets me the result I expect.
So the question is: what changes about the 'userinput' variable inside the on('click') function? What am I not seeing here?
Thanks in advance!
As the function is self invoking, the variable will be set at page load. It is not reassessed every time the button is clicked. During page load you will probably find that:
The input is emtpy
The input is lower down the page than the script (More probable I think)
So at the time of setting there is no value. You probably want to make the variable scoped to the button click:
(function () {
"use strict";
var submitbutton = $('#submitcommand'),
textinput = $('#textinput')
submitbutton.on('click', function() {
var userinput = textinput.val();
alert("textinput is " + textinput.val()); // => works
alert("userinput is " + userinput); // => undefined
});
}());
Also, you will want to make sure the script block is at the bottom of the page, as then all the UI elements will be loaded in the browser when the function is invoked.
Your value is undefined inside the handler.
You should perhaps use it this way:
(function() {
"use strict";
var submitbutton = $('#submitcommand'),
textinput = $('#textinput')
userinput = textinput.val();
submitbutton.on('click', function() {
userinput = textinput.val();// sets the value in the handler
alert("textinput is " + textinput.val());
alert("userinput is " + userinput);
});
}());
working example:http://jsfiddle.net/avCay/
Note that this works this way due to the scope of the variable and the "reset" of the values inside the event handler. The first pass parses the script, the second executes it, and the subsequent event handler fires on the click event of the submitbutton element, setting the value at that point during that event.
EDIT: NOTE: regarding the jQuery read/load wrap (as in my example fiddle) if you wish to NOT have that, you can do:
$('#submitcommand').on('click', function() {
instead of the variable and jQuery will properly hook the event handler to the element.
EDIT2: or perhaps this will provide more insight here. If you do:
You will still need to re-access the variable however if you wish to have the "current" value of the input, not that in the "undefined" state during the initial execution due to the parse/execute cycle of the Javascript. The "re-access" of the .val(); vs the value during the initial script execution is the key to that "undefined" value. and setting it "inside" the handler gets the current value.
When
var userinput = textinput.val()
is evaluated:
textinput may not exist yet - you must be either in a document.ready() handler or the <script> block must be after #textinput in the DOM.
if it does exist, userinput gets its current value, not any future value it may happen to have.
This is because the value of #textinput is empty on page load (unless you set the value to something when the page loads). So the stored value in userinput is empty. When you read the value from textinput inside the event handler, it looks up the value of the input-field.
Makes sense? :)

Categories