Function only partially executing - javascript

I guess I'm a beginner at javascript and as the title suggests, my function won't fully execute.
function showBorder2(classid){
var first2 = document.getElementById("swordsmanIcon");
var second2 = document.getElementById("marksmanIcon");
var third2 = document.getElementById("scribeIcon");
first2.style.borderColor = "transparent";
second2.style.borderColor = "transparent";
third2.style.borderColor = "transparent";
classid.style.borderColor = "Aqua";
alert("1");
var classType = elementid.id;
alert("2");
window.classType = classType;
}
I've ran a little test and i have discovered that alert 1 triggers, but alert 2 does not... This is probably a simple mistake but I really can't see it.
Thanks in advance!

if alert1 is triggering but not alert2, the problem must be that elementid is not defined and trying to access a property of an undefined variable will throw an error and break execution of your js. The solution:
a) make sure to define elementid
b) if you can't guarantee definition of elementid, add a check to see if it is defined before trying to access a child property.
A simple shorthand for this is var classType = elementid && elementid.id;
^ that's short for something like this:
var classType;
if(elementid) {
classType = elementid.id;
}

Probably one of your element variable (first2, second2, third2, classid) is either null or undefined. It is good defensive programming (especially in javascript) to check for null or undefined before using the variable.
I'm guessing when the showBorder2 function is called, the parameter passes to "classid" either is not a valid element or nothing is pass to it.

Related

Variable cannot be changed inside Window object

I'm developing a cordova app and within it, I'm using an iFrame. I'm using the following code to communicate between the iFrame and the app itself:
var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
var eventer = window[eventMethod];
var messageEvent = eventMethod == "attachEvent" ? "onmessage" : "message";
var message = "";
var output = "";
eventer(messageEvent, function(e) {
message = e.data;
var data = JSON.parse(message);
for (var i = 0; i < data.messages.length; i++){
var msg = data.messages[i];
output = msg.msg;
alert(output); //*1
}
}, false);
I'm copying the code from here: https://davidwalsh.name/window-iframe
The alert inside the eventer at *1 show the output variable is what I want. But once outside of this eventer function, the variable output reverts to blank.
After some research I think it might because I'm creating a Window object, but I'm not sure if that's the case or exactly what it is.
How can I permenently modify the variable "output"? Thanks.
Try changing the for loop to this:
for (var i = 0; i < data.messages.length; i++){
var dataMsg = data.messages[i].msg;
output = dataMsg; alert(output); //*1
}
I don't know if there is an issue with assigning msg.msg to output.
The problem is with your understanding of how events work.
Judging from your comments, this is how you think it works:
// Ordering
var output = ""; // 1. Assign empty string to output
eventer(messageEvent, function(e) {
// assign value to output // 2. Assign value to output
});
alert(output); // 3. Print output
In reality, it works like this:
var output = ""; // 1. Assign empty string to output
eventer(messageEvent, function(e) {
// assign value to output // 3. Assign value to output
});
alert(output); // 2. Print output
The value is changed by your callback function, but you are accessing it in the wrong order. eventer, or in modern browsers, .addEventListener, registers a callback function. Your callback function will only get triggered after the current thread is finished. Of course it will print out an empty value, since alert is executed before you even changed its value.
If you are not familiar with the basics of event-based programming, I suggest you to take a look at this tutorial.
Just a side note, never use an iframe unless you have specific reasons of doing so. And also you do not need the first few lines since you are using Cordova. attachEvent is IE-specific.
As a general advice, try not to pollute the global scope with unnecessary variables in the future.

Continue executing a dom command

I use this dom command in some testing pages:
document.querySelector('div#summary-item div.description').innerHTML || ""
But in some pages when the first part does not exist I don't receive the second "" but I receive this error and my programm stops
Uncaught TypeError: Cannot read property 'innerHTML' of null(…)
Is there any simple way to receive the "" without the need to use the typeof in an if statement?
You don't need typeof but you do need an if statement or some other flow control.
var el = document.querySelector('div#summary-item div.description');
var data = "";
if (el)
data = el.innerHTML;
Or here it is using the conditional operator:
var el = document.querySelector('div#summary-item div.description');
var data = el ? el.innerHTML : "";
Technically, you could get away without using a flow control statement or expression by using an object that has a .innerHTML property.
var data = (document.querySelector('div#summary-item div.description') || {innerHTML:""}).innerHTML;
But I think that's ugly. if statements are part of the language. Not sure why you'd want to avoid it.
Of course, you could always use a function that abstracts it away if it really bothers you.
function htmlFromElem(selector) {
var el = document.querySelector(selector);
return el ? el.innerHTML : "";
}
Then use it like this:
var data = htmlFromElem('div#summary-item div.description')

Specify function only for one instance

I am a newbie in JavaScript and so don't really understand its object model, but as I understood I have to do something like.
function set_test(text) { this['test'] = text; };
a = {};
text = 'ok';
a.prototype.ok = set_test(text);
alert(a['test']); #Should be 'ok'
text = 'fail';
a.ok;
alert(a['test']); #Should be 'ok'
Can somebody say what's wrong here?
Objects don't have a prototype by default, and a.prototype.ok = set_test(text); would make ok equal to the return value of set_test() which is undefined.
Try doing it this way instead:
function set_test(text) { this['test'] = text; };
var a = {
ok: set_test
},
text = 'ok';
a.ok(text);
alert(a['test']); //Should be 'ok'
text = 'fail';
a.ok(text);
alert(a['test']); //Should be 'fail'
I think the whole code is wrong.
You see, in JavaScript, functions are objects and you can pass them around as you wish. The execution context (this) depends on the way you invoke a function.
If you store a function in a property of an object and then invoke it by calling obj.fn(), the execution context is set to obj.
Also notice that you aren't supposed to invoke the function when assigning it as the object's property.
function set_test(text) { this['test'] = text; }
var a = {};
a.ok = set_test; // see, no invocation
a.ok('abc');
alert(a['test']); // alerts 'abc'
a.ok('def');
alert(a['test']); // alerts 'def'
I think in function set_test this['test'] will not work. beacause "this" is not "a". You should define method set_test under object a - then it will work.
I recomend you check Mootools docs - there is much better object model syntax. You can also check Mootools source to get more detail answer to you question and js object model

jQuery global selection "cache"

I don't think I'm the first one to run into this issue but I haven't find a way to search for this without getting results that have nothing to do with the issue.
I adopted the not so extended good practice of "caching" repetitive jQuery selections into vars like var element = $('#element'); to prevent "DOM pool searching" for every repeated use of the element
The problem I'm having is that now I'm doing this caching inside a function. Something like:
function functionname (id) {
var id = $('#'+id);
//extra stuff
}
I'm not expert in variables scopes but I'm not being able to do
functionname ('some-div-id');
some-div-id.dialog('open');
So I'm pretty sure it's because the variable created inside the function is not accesible outside the function itself.
Then I came up with
function functionname (id) {
window.id = $('#'+id);
//extra stuff
}
but if I try to do window.some-div-id.dialog('open'); I get TypeError: 'undefined' is not a function
What am I missing? I'm sure it's a small dumb thing but I'm missing it just in front of my eyes.
Thanks
EDIT
Thanks everyone but you're missing something.
The code suggestions are missing the fact that the inside "global" variable name is dynamic:
var CACHEobject = {};
function doSomething (NAMEHERE) { //note the function parameter
CACHEobject.NAMEHERE = $('#'+NAMEHERE);
}
So the idea is that the function creates a javascript variable with the same name that the #element_id. If I pass a name to the function it should select the html id with that name and "cache it" to a global variable with the same name:
doSomething('myDialogOne'); doSomething('myDialogTwo');
so I can later do
CACHEobject.myDialogOne.dialog('open'); CACHEobject.myBox.dialog('close');
This is what you want (based off the edit):
var CACHEobject = {};
function doSomething(id) {
CACHEobject[id] = $('#' + id);
}
Your idea is fine. Just set up an object for that. Here's an example using STASH as the caching object:
<html>
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script>
var STASH = {};
$(document).ready(function(){
// stash your elements
STASH.item = $('#item');
STASH.otherItem = $('#otherItem');
// do stuff to them
STASH.item.css({
color: '#f00'
}); // sets #item to red
alert(STASH.otherItem.text()); // alerts foo
});
</script>
<style></style>
<body>
<div id="item">bar</div>
<div id="otherItem">foo</div>
</body>
</html>
window.some-div-id.dialog('open');
is interpreted as:
window.some - div - id.dialog('open');
i.e. subtracting, which causes three undefined variables, one of which is id.dialog which causes an error when trying to be executed as a function.
For special characters, use:
window["some-div-id"].dialog('open');
And to define:
window[id] = $("#" + id);
Anyhow, I would not advise you to use global variables. You'd better overwrite the jQuery function to implement caching (using an object with the selector as key and the matched element as value).
You could just declare the variable outside the function.
var $foo;
function some_function(id) {
$foo = $('#' + id);
}
function setDialog(selector) {
window.$dialogElem = $(selector);
//window.dialogSelector = selector;
}
var id= 'mensajes';
setDialog('#'+id);
window.$dialogElem.dialog();
//$(window.dialogSelector).dialog();
commented stuff is an alternative that takes less memory. But why the hell use window?? check this fiddle for various simple techniques.

Recursion function not defined error

Hi i have a problem with recursion.
i followed this example from wc3 http://www.w3schools.com/jsref/met_win_settimeout.asp
But mine seems to not work at all.
function rotateImages(start)
{
var a = new Array("image1.jpg","image2.jpg","image3.jpg", "image4.jpg");
var c = new Array("url1", "url2", "url3", "url4");
var b = document.getElementById('rotating1');
var d = document.getElementById('imageurl');
if(start>=a.length)
start=0;
b.src = a[start];
d.href = c[start];
window.setTimeout("rotateImages(" + (start+1) + ")",3000);
}
rotateImages(0);
Firebug throws the error :
rotateImages is not defined
[Break On This Error] window.setTimeout('rotateImages('+(start+1)+')',3000);
However if i change the timeOut to :
window.setTimeout(rotateImages(start+1),3000);
It recursives but somehow the delay doesn't work and gives me too much recursion(7000 in a sec)
There are many reasons why eval should be avoided, that it breaks scope is one of them. Passing a string to setTimeout causes it to be evaled when the timer runs out.
You should pass a function instead.
window.setTimeout(rotateImages(start+1),3000);
This calls rotateImages immediately, then passes its return value to setTimeout. This doesn't help since rotateImages doesn't return a function.
You probably want:
window.setTimeout(rotateImages,3000,[start+1]);
Or create an anonymous function that wraps a closure around start and pass that instead:
window.setTimeout(function () { rotateImages(start + 1); },3000);
The latter option has better support among browsers.
Be wary of code from W3Schools.
The other answers give a solution. I'll just add that you're recreating the Arrays and repeating the DOM selection every time the rotateImages function is called. This is unnecessary.
You can change your code like this:
(function() {
var a = ["image1.jpg","image2.jpg","image3.jpg", "image4.jpg"];
var c = ["url1", "url2", "url3", "url4"];
var b = document.getElementById('rotating1');
var d = document.getElementById('imageurl');
function rotateImages(start) {
b.src = a[start];
d.href = c[start];
window.setTimeout(function() {
rotateImages( ++start % a.length );
}, 3000);
}
rotateImages(0);
})();
Try this syntax:
window.setTimeout(function() {
rotateImages(start+1);
},3000);
setTimeout() expects a function reference as the 1st parameter. Simply putting a function call there would give the return value of te function as the parameter, this is why the delay did not work. However your first try with evaluating a string was a good approach, but it is not recommended.

Categories

Resources