This should be a simple if statement, but it's not working for me. Essentially, when you click an element, I want that element to be highlighted and the ID to be put into a the variable value. However, if in the situation the same element is clicked twice, I want to value = NULL.
(function($){
$(".list").click(function() {
$(this).toggleClass("hilite");
var temp = $(this).attr('id');
if (value != temp) {
var value = $(this).attr('id');
} else {
value = NULL;
}
});
})(jQuery);
Your primary problem is that you're "hoisting" the value variable by redefining it with the var keyword. This code can also be written more efficiently with a lot less code. This should work:
(function($) {
// somewhere outside click handler
var value = '';
// click handler
$(".list").click(function() {
var id = $(this).toggleClass('hilite').attr('id');
value = (value === id) ? null : id;
/* or if you prefer an actual if/else...
if (value === id) {
value = null;
else {
value = id;
}
*/
});
})(jQuery);
Edit: a couple general comments about the original snippet that might be useful:
NULL should be null
Try not to run the same selector multiple times, or recreate a jQuery object from the same DOM object multiple times - it's much more efficient and maintainable to simply cache the result to a variable (e.g., var $this = $(this);)
Your comparison there is probably "safe", but better to use !== than != to avoid unintentional type coercion.
Not sure how exactly you intended to use value in the original example, but always remember that variables are function-scoped in JavaScript, so your var value statement is hoisting the value identifier for that entire function, which means your assignments have no effect on anything outside that click handler.
You need to declare var value outside the scope of the function, so that its value is maintained across function calls. As it is, the value variable is lost right after it is set, because it goes out of scope.
var value = null;
(function($){
$(".list").click(function() {
$(this).toggleClass("hilite");
var temp = $(this).attr('id');
if (value != temp) {
value = temp;
} else {
value = null;
}
});
})(jQuery);
You could do:
(function($){
var tmp = {};
$(".list").click(function() {
$(this).toggleClass("hilite");
var id = $(this).attr('id');
if (!tmp[id]) {
var value = id;
tmp[id] = true;
} else {
value = NULL;
tmp[id] = false;
}
});
})(jQuery);
In this way you use a tmp object that stores the state for all the different id's
It might not be skipping that statement, you might just be getting a confusion over the implied global "value" and the local "value".
(function($){
$(".list").click(function() {
$(this).toggleClass("hilite");
var temp = $(this).attr('id');
if (value != temp) { // <-----------------Implied global var called "value"
var value = $(this).attr('id'); // <---Local variable valled "value"
} else {
value = NULL; // <---------------------Which one am I
}
});
})(jQuery);
Also, it ought to be value = null as NULL is just an undefined variable.
This should be a working example of both points:
var value = null;
(function($){
$(".list").click(function() {
$(this).toggleClass("hilite");
var temp = $(this).attr('id');
if (value != temp) {
value = $(this).attr('id');
} else {
value = null;
}
});
})(jQuery);
Do you not need to declare value before you use it in the conditional statement?
you aren't setting a value in this function.
var value = "NULL";
(function($){
$(".list").click(function() {
$(this).toggleClass("hilite");
var temp = $(this).attr('id');
if (value != temp) {
value = $(this).attr('id');
} else {
value = "NULL";
}
});
})(jQuery);
The variable value is not defined. And it either needs to be a global variable or you could use jQuery's $('.selector).data() method to attach it to the element:
http://api.jquery.com/data/
I also recommend using !== for the comparison, since that compares the type of the variable as well as the content.
Related
The variable cont is being lost in the following:
__factory.setupMenu = function(cont,input,multiSelect,exclusive,popMenu){
var __menu = {multiSelect:multiSelect};
spotter.events.setEventTrigger(input,'change');
__menu.exclusive = {inputs:[],values:exclusive||[],simpleValues:[]};
alert(cont);//<-- is defined here
window.popSelectComponent= cont;//<-- saved it globally to test reference
return function(ajaxResult){
var data = ajaxResult.template.response||[];
var info = {},l=data.length;
while(--l > -1){
info[String(data[l].value)] = data[l].abbr||data[l].name;
}
var textTarget;
alert(window.popSelectComponent);//<-- this is defined as expected
alert(cont);//<-- is now undefined
alert(input);//<-- this is defined as expected
if(!(textTarget = cont.querySelector('[data-pop-selected]'))){textTarget = cont;}
if(!input.popSelectTemplate){
spotter.data.bindElementToInput(textTarget,input,function(content){
content = content.split(',');
var l=content.length;
while(--l > -1){
content[l] = info[content[l]];
}
content = content.join(',');
return (content.length ? content : 'ignore');
});
}
else{
var cont = document.createElement('SPAN');//<-- PROBLEM IS CAUSED HERE. HOISTING IS CAUSING CONT TO BE UNDEFINED AT CLOSURE START
cont.className="multi-select";
cont.appendChild(cont);
//removal function
var remove = (function(input){
return function(e){
var evt = e ? e:window.event;
if (evt.stopPropagation) evt.stopPropagation();
if (evt.cancelBubble!=null) evt.cancelBubble = true;
if(input.value !== input.spotterPopSelectDefaultValue){
input.value = input.value.removeListValue(this.getAttribute('data-id'),',');
spotter.deleteElement(this);
if(input.value === '' && input.value !== input.spotterPopSelectDefaultValue){
input.value = input.spotterPopSelectDefaultValue;
input.eventTriggers['pop-select-change']();
}
}
};
}(input));
input.spotterPopMenuOptions = __menu;
input.addEventListener('pop-select-change',(function(cont, info, template){
return function(){
var HTML = '';
this.value.split(',').forEach(function(val){
HTML += template.replace('$[ID]', val).replace('$[NAME]', info[val]);
});
cont.innerHTML = HTML;
spotter.castToArray(cont.children).forEach(function(el){ console.log('option el',el); el.addEventListener('click',remove,false); });
console.log('input.spotterPopMenuOptions',input.spotterPopMenuOptions);
};
}(cont, info, input.popSelectTemplate.innerHTML)),false);
}
....
So running var func = __factory.setupMenu(...)({template:{}}) I am receiving an error message that cont is undefined while window.popSelectComponent is defined like expected. I tried changing the name of cont thinking I was overlooking something that was changing the value but that did not work either.
After running the function, I check cont in the context that initially created this closure and cont is still defined so it is not a matter of an object reference being lost as far as I can tell.
Perhaps a highly simplified example would make the problem more obvious:
var outer = function(theVariable) {
console.log("In the outer function, theVariable is", theVariable);
var inner = function() {
console.log("In the inner function, theVariable is", theVariable);
if (false) {
var theVariable = 2;
}
};
inner();
}
outer(1)
In the outer function, theVariable is 1
In the inner function, theVariable is undefined
As you have seen, the fact that a different variable with the same name has been declared (even though not initialized) in the inner function hides the properly initialized variable in the outer function that would have otherwise been visible.
You might think that because the variable is declared in a block, it would not affect other parts of the function. No, var is function scoped, not block scope.
This flaw has been addressed in modern versions of Javascript, and the var keyword has been superseded by let, which has the block scope you were expecting. var is kept for backwards compatibility, but you should not use it in new code.
When I have the conditionals inside of an event listener inside of a function, they don't seem to work properly [I commented them in]. In fact they don't work at all, but when I delete the conditionals everything works (except it works incorrectly because I need the conditionals) I'm a lot more familiar with Java if that helps in the explanation.
var board = document.getElementById("checkersBoard");
var isRedHighlighted = false;
var isBlackHighlighted = false;
var lastClick;
board.addEventListener("click", function(e)
{
//alert(e.target.src);
if(isBlackHighlighted || isRedHighlighted)
{
if(isBlackHighlighted)
{
e.target.src= "Directory/BlackPiece.png";
lastClick.src = "Directory/BlankSpace.png";
isBlackHighlighted = false;
}
if(isRedHighlighted)
{
e.target.src= "Directory/RedPiece.png";
lastClick.src = "Directory/BlankSpace.png";
isRedHighlighted = false;
}
}
else
{
if (e.target.src == "Directory/BlackPiece.png") // why does this not work
{
e.target.src = "Directory/BlackPieceH.png"
lastClick = e.target;
isBlackHighlighted = true;
}
if (e.target.src == "Directory/RedPiece.png") // why does this not work
{
e.target.src = "Directory/RedPieceH.png"
lastClick = e.target;
isRedHighlighted = true;
}
}
});
Judging by the strings you're comparing against, such as: "Directory/BlackPiece.png", it seems that you're intending to compare the variable(s) against the value of the src attribute (e.target.getAttribute('src')), rather than the src property (e.target.src).
This would yield a comparison such as, for example:
e.target.getAttribute('src') = "Directory/BlackPiece.png";
The difference between the two is that the src attribute looks at the attribute-value found within, and retrieves the value from, the specified attribute.
Whereas the src property resolves to an absolute URL, which would give a value similar to: http://www.example.com/Directory/BlackPiece.png.
References:
Element.getAttribute.
HTMLImageElement.src.
I'm having a bit of trouble with variable scope within jQuery/JavaScript. I'll just give a code example demonstrating my issue as it's probably easier to understand than trying to explain with words.
var example_1 = function() {
row = 10;
}
var example_2 = function() {
something.click(function(){
var something_3 = function() {
alert(row);
}
something_3();
}
}
The problem is, the row variable cannot be accessed within the something_3 function. I have other variables from the example_1 function being accessed in the example_2 function, but when put in the something_3 function it no longer works. Thanks for any help!
Edit: Added extra code to show what works and what isn't. Row is defined within the Build function as are board and mines. Board and mines can be accessed but row cannot.
var build = function(row) {
row = typeof row !== 'undefined' ? row : 5;
board = $('.container');
mines = [1,2,3,4];
}
build();
var start = function() {
var tile = $('.container div');
tile.click(function() {
var this_index = $(this).index();
var has_mine = $.inArray(this_index, mines);
var scan = function() {
alert(row);
}
if (has_mine > -1) {
add_alert("Has mine, you're dead!");
} else {
scan();
$(this).html('Clear!');
}
});
var add_alert = function(msg) {
board.append("<span class='alert'>" + msg + "</span>")
$('body').append("<div class='blackout'></div>");
}
}
start();
var build = function(row) {
row = typeof row !== 'undefined' ? row : 5;
//[...]
}
rowis not being defined as a variable, but instead is a parameter. This way, it doesn't leak as a global variable.
Simply changing the name of parameter should work:
var build = function(rowValue) {
row = typeof rowValue !== 'undefined' ? rowValue: 5;
//[...]
}
However, you shouldn't be using implicitly global variable. Global variables are already bad enough.
Option one, declaring variables:
var row, board, mines;
var build = function(rowValue) {
row = typeof rowValue !== 'undefined' ? rowValue : 5;
board = $('.container');
mines = [1,2,3,4];
}
Option two, using the global identifier:
var build = function(row) {
window.row = typeof row !== 'undefined' ? row : 5;
window.board = $('.container');
window.mines = [1,2,3,4];
}
Multiple issues:
If you don't specify var in front of a variable inside a function it becomes a global variable
row doesn't get intialzed because you didn't call example_1(); yet
In an unrelated note, don't forget to use semicolons at end of the anonymous functions as it might be interpreted as self executing functions.
EDIT 2:
var build = function(row) {};
ok so your issue is the row variable is a parameter so isn't a global variable anymore. If you remove your row parameter / update variable name passed to build method, it will work.
trying to get my head around objects, methods, closures, etc... in Javascript.
Can't see why this isn't working, some fundamental flaw in my thinking I guess. I'm expecting the val variable to be passed through to the addNote() function but it isn't. I thought that any variables declared outside of a function are available to that function, as long as they're not within another function. Is that not correct?
if(typeof(Storage) !== "undefined") {
console.log(localStorage);
var $input = $('#input'),
$submit = $('#submit'),
$list = $('#list'),
val = $input.val();
var noteApp = {
addNote : function(val) {
var item = val.wrap('<li />');
item.appendTo($list);
clearField();
},
clearField : function() {
$input.val = '';
},
delNote : function(note) {
}
};
$submit.on('click', function(){
noteApp.addNote();
});
} else {
}
I'm trying to learn how the pros manage to get their code so clean, concise and modular. I figured a note app would be a perfect start, shame I got stuck at the first hurdle...
Cheers.
There are several issues with the code in the question
defining an argument named val and not passing an argument to the function
when calling clearField() inside the object literal it's this.clearField()
You're only getting the value once, not on every click
val is a string, it has no wrap method
$input.val = ''; is not valid jQuery
I would clean it up like this
var noteApp = {
init: function() {
if (this.hasStorage) {
this.elements().events();
}
},
elements: function() {
this.input = $('#input');
this.submit = $('#submit');
this.list = $('#list');
return this;
},
events: function() {
var self = this;
this.submit.on('click', function(){
self.addNote();
});
},
hasStorage: (function() {
return typeof(Storage) !== "undefined";
})(),
addNote: function() {
this.list.append('<li>' + this.input.val() + '</li>');
this.clearField();
return this;
},
clearField: function() {
this.input.val('');
},
delNote : function(note) {
}
}
FIDDLE
Remember to call the init method
$(function() { noteApp.init(); });
In your call to addNote(), you don't pass any argument for the val, so it will be undefined:
noteApp.addNote();
// ^^ nothing
Pass the input (seems you want the jQuery object not the string value because of your val.wrap call):
noteApp.addNote($input);
When you declare the val in the function, it is scoped to that function and will only be populated if the function call passes a value for that argument. Even if you have another variable in an upper scope with the same name val, they are still differentiated. Any reference to val in the function will refer to the local val not the upper scope.
This question already has answers here:
Listening for variable changes in JavaScript
(29 answers)
Closed 6 years ago.
I have two variables:
var trafficLightIsGreen = false;
var someoneIsRunningTheLight = false;
I would like to trigger an event when the two variables agree with my conditions:
if(trafficLightIsGreen && !someoneIsRunningTheLight){
go();
}
Assuming that those two booleans can change in any moment, how can I trigger my go() method when they change according to the my conditions?
There is no event which is raised when a given value is changed in Javascript. What you can do is provide a set of functions that wrap the specific values and generate events when they are called to modify the values.
function Create(callback) {
var isGreen = false;
var isRunning = false;
return {
getIsGreen : function() { return isGreen; },
setIsGreen : function(p) { isGreen = p; callback(isGreen, isRunning); },
getIsRunning : function() { return isRunning; },
setIsRunning : function(p) { isRunning = p; callback(isGreen, isRunning); }
};
}
Now you could call this function and link the callback to execute go():
var traffic = Create(function(isGreen, isRunning) {
if (isGreen && !isRunning) {
go();
}
});
traffic.setIsGreen(true);
//ex:
/*
var x1 = {currentStatus:undefined};
your need is x1.currentStatus value is change trigger event ?
below the code is use try it.
*/
function statusChange(){
console.log("x1.currentStatus_value_is_changed"+x1.eventCurrentStatus);
};
var x1 = {
eventCurrentStatus:undefined,
get currentStatus(){
return this.eventCurrentStatus;
},
set currentStatus(val){
this.eventCurrentStatus=val;
}
};
console.log("eventCurrentStatus = "+ x1.eventCurrentStatus);
x1.currentStatus="create"
console.log("eventCurrentStatus = "+ x1.eventCurrentStatus);
x1.currentStatus="edit"
console.log("eventCurrentStatus = "+ x1.eventCurrentStatus);
console.log("currentStatus = "+ x1.currentStatus);
The most reliable way is to use setters like that:
var trafficLightIsGreen = false;
var someoneIsRunningTheLight = false;
var setTrafficLightIsGreen = function(val){
trafficLightIsGreen = val;
if (trafficLightIsGreen and !someoneIsRunningTheLight){
go();
};
};
var setSomeoneIsRunningTheLight = function(val){
trafficLightIsGreen = val;
if (trafficLightIsGreen and !someoneIsRunningTheLight){
go();
};
};
and then instead of assigning a value to a variable, you just invoke the setter:
setTrafficLightIsGreen(true);
There is no way to do it without polling with setInterval/Timeout.
If you can support Firefox only, you can use https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/watch
Which will tell you when a property of an object changes.
Your best solution is probably making them part of an object and adding getters, setters that you can send out notifications yourself, as JaredPar showed in his answer
You could always have the variables be part of an object and then use a special function to modify the contents of it. or access them via window.
The following code can be used to fire custom events when values have been changed as long as you use the format changeIndex(myVars, 'variable', 5); as compared to variable = 5;
Example:
function changeIndex(obj, prop, value, orgProp) {
if(typeof prop == 'string') { // Check to see if the prop is a string (first run)
return changeIndex(obj, prop.split('.'), value, prop);
} else if (prop.length === 1 && value !== undefined &&
typeof obj[prop[0]] === typeof value) {
// Check to see if the value of the passed argument matches the type of the current value
// Send custom event that the value has changed
var event = new CustomEvent('valueChanged', {'detail': {
prop : orgProp,
oldValue : obj[prop[0]],
newValue : value
}
});
window.dispatchEvent(event); // Send the custom event to the window
return obj[prop[0]] = value; // Set the value
} else if(value === undefined || typeof obj[prop[0]] !== typeof value) {
return;
} else {
// Recurse through the prop to get the correct property to change
return changeIndex(obj[prop[0]], prop.slice(1), value);
}
};
window.addEventListener('valueChanged', function(e) {
console.log("The value has changed for: " + e.detail.prop);
});
var myVars = {};
myVars.trafficLightIsGreen = false;
myVars.someoneIsRunningTheLight = false;
myVars.driverName = "John";
changeIndex(myVars, 'driverName', "Paul"); // The value has changed for: driverName
changeIndex(myVars, 'trafficLightIsGreen', true); // The value has changed for: traggicIsGreen
changeIndex(myVars, 'trafficLightIsGreen', 'false'); // Error. Doesn't set any value
var carname = "Pontiac";
var carNumber = 4;
changeIndex(window, 'carname', "Honda"); // The value has changed for: carname
changeIndex(window, 'carNumber', 4); // The value has changed for: carNumber
If you always wanted to pull from the window object you can modify changeIndex to always set obj to be window.
function should_i_go_now() {
if(trafficLightIsGreen && !someoneIsRunningTheLight) {
go();
} else {
setTimeout(function(){
should_i_go_now();
},30);
}
}
setTimeout(function(){
should_i_go_now();
},30);
If you were willing to have about a 1 millisecond delay between checks, you could place
window.setInterval()
on it, for example this won't crash your browser:
window.setInterval(function() {
if (trafficLightIsGreen && !someoneIsRunningTheLight) {
go();
}
}, 1);