My Javascript works on IE FF & Safari, but not Chrome - javascript

I have a simple rotating banner javascript on the site I'm developing. It works perfectly locally, on IE, Firefox and Safari but not on Chrome. Is there something wrong with my code? I'm new to Javascript so this problem is really baffling me. Here's the relevant code:
<script language="Javascript" type="text/javascript">
adImages = new Array
("images/AMSite18.png","images/AMSite19.png","images/AMSite09b.png")
thisAd = 0
imgCt = adImages.length
function rotate() {
if (document.images) {
thisAd++
if (thisAd == imgCt) {
thisAd = 0
}
document.adBanner.src=adImages[thisAd]
setTimeout("rotate()", 3 * 1000)
}
}
</script>
</head>
<body onload="rotate()">
<div id="banner">
<img src="images/banner1.gif" name="adBanner" alt="Ad Banner" />
</div><!-- end banner -->
</body>

It looks like the reason it isn't working in Chrome is this line:
document.adBanner.src=adImages[thisAd]
You're referring to the element with the name "adBanner" via document.adBanner, but Chrome doesn't support that. You'll have to use something like this:
document.getElementsByName('adBanner')[0].src = adImages[thisAd];
Some others things that could improve code quality:
Don't use the language attribute. Not necessary.
Use the format var x = [...]; to create a new array. There's no reason to use the constructor format. None at all. Zippo. No one could possibly comment on this answer with a reason you'd use new Array(...) instead. No one.
Use the var keyword to create variables, and the semi-colon to end your statements. Although it isn't hurting anything here, if you don't use var, then JavaScript assumes you're creating/changing a global variable, when that may not necessarily be the case. (Also, the semi-colon rules may be a little convoluted, but it really helps with readability.)
Why are you checking for document.images? It's unnecessary. You don't refer to it anywhere else.
Crockford suggests using x += 1 instead of x++. Not a big deal, and a lot of people disagree, just something I noticed.
Always use strict equality (===). The kind you're using (==) doesn't take into account types; 2 == "2" will return true, but 2 === "2" will not. Again, not a big deal, and some people don't really care, but it could bite you later on, in a different project.
Never pass strings to setTimeout. The browser just evals the string, and nobody hangs out with people who eval stuff. You don't even need to pass a string, because you're using a function that doesn't need any arguments! Just use this:
setTimeout(rotate, 3 * 1000);
Try to put script tags at the bottom of the body. There are two reasons for this. First, performance. When the browser gets to your script, it stops everything to parse and execute the code. If you put it at the bottom of the body instead of the head, the page will at least appear to load faster. The second point is addressed next:
Try to avoid using onload. It's just gauche. The reason you need to is because your script is in the head, and has no access to the DOM just yet. But if that script was moved to the bottom of the body (which, for some reason, you might not be able to; no big deal), you wouldn't have to mess with onload at all:
<body>
<div id="banner">
<img ... />
</div>
<script>
// Copy all of your code exactly the same,
// and then:
rotate();
</script>
</body>
For the love of god, don't use the name attribute. For forms, who cares? But when you're manipulating elements with JavaScript, use the id. It's immediately obvious what you're doing, and document.getElementById('adBanner') is way faster than document.getElementsByName('adBanner')[0].

You should start by fixing the syntax problems.
Lines should end with a semi-colon ;, variable should be declared with var and you should use [] rather than new array, pass a function to setTimeout rather than a string
var adImages = ['images/AMSite18.png','images/AMSite19.png','images/AMSite09b.png'];
var thisAd = 0;
var imgCt = adImages.length;
function rotate() {
if (document.images) {
thisAd++;
if (thisAd == imgCt) {
thisAd = 0;
}
document.adBanner.src=adImages[thisAd];
setTimeout(function(){
rotate();
}, 3 * 1000);
}
}
This may not fix it, but you you should do it anyway :)

I just ran you code on Chrome 11 on a Mac and it worked. Even with the syntax errors. But Paul is right you should always write valid JavaScript.
Also this is a better way of passing functions
setTimeout(rotate, 3 * 1000);

Related

correct js in tag manager

i am trying to add a tag (to custom javascript) to my google tag manager, but i get "Error at line 12, character 5: Parse error. primary expression expected". Can i please get help to correct my code?
<script>
var x=document.getElementById("ad"),
z=window.getComputedStyle(x,null),
y=z.getPropertyValue("display");
function showAdblockAlert()
{alert("You're missing ads, therefore turn off your AD-blocker!")
}function adBlockNotDetected()
{alert("Thank you for not using AD-blocker");
console.log
("no ad-blocker")}console.log(y);
"none",
==y?showAdblockAlert():adBlockNotDetected();
</script>
This code is odd. It combines some basic mistakes as well as advanced techniques if you can call that a ternary or using commas with a var.
Anyhow, your error is likely due to poor copying. The "none",== part, I think, got there from somewhere else.
Here, try this:
var x = document.getElementById("ad"),
z = window.getComputedStyle(x, null),
y = z.getPropertyValue("display");
function showAdblockAlert() {
alert("You're missing ads, therefore turn off your AD-blocker!")
} function adBlockNotDetected() {
alert("Thank you for not using AD-blocker");
console.log("no ad-blocker")
}
console.log(y);
y ? showAdblockAlert() : adBlockNotDetected();
You should know, however, that alerts in production is a very good way to get your site blacklisted one way or another.
Besides, declaring globals like that is a very bad idea.
Your code is unsafe. It relies on getComputedStyle to be there, as well as other elements. It throws errors otherwise.
Finally, the use of functions there seems to be a little excessive. They're not needed if you swap the ternary with a normal if, making the code much more readable.

Javascript commenting practice

Recently, using comments in javascript I've run into a few questions about the commenting system. I wanted to comment on the name of a variable, so I put it right after declaring the name, on the same line, like this:
var wk /* (website key) */ = 1;
Now I think this is perfectly valid and works fine, right?
So a little while later, I wanted to comment out the whole block of code that line was on, like this:
/*
~ more code ~
var wk /* (website key) */ = 1;
~ more code ~
*/
But this doesn't work, because when the interior comment closes, it closes the whole comment. That seems kind of dumb to me. Is there any way to do nested comments in javascript?
You can't nest block comments, but you can do this:
var wk = 1; // website key
Or
// website key
var wk = 1;
It looks less awkward, and block comments are only really supposed to be used for... well, blocks. It's just better coding style in general.
Or, even better, make your code self-documenting and eliminate the need for a comment at all:
var websiteKey = 1;
I ran into a problem recently where I had written some JavaScript for a friend on their wordpress blog. Anyway long story short they used the WYSIWYG editor, witch reformatted the page source.
so
<script type="test/javascript">
$(function() {
// this is a button click handler
$('#button').click(function () {
// did some stuff
});
})();
</script>
turned into
<script type="test/javascript">
$(function() {// this is a button click handler $('#button').click(function() {
// did some stuff }); })(); </script>
The point of the story is always be aware of your environment and the needs of your users, even when commenting
Do it like this:
var wk = 1; //Website Key - Additional Info Here
then if you need to comment the block, you can do
/*
Comments and stuff go here...
var wk = 1; //Website Key - Additional Info Here
*/
Avoid nested block comments. They are forbidden in the ECMA Script specification. If any interpreters allow this now, they may not in the future.
From Section 7.4,
Comments can be either single or multi-line. Multi-line comments cannot nest.
You never put single-line comment between the code. Reasons are that it is not readable enough and not being followed in the community as a best practice.
Putting away code via commenting it out is easily done with multi-line comment tags /* ... */ or single line ones //.
PS: For commenting I suggest to avoid doing magic stuff. I would not encourage nested commented at all! For having a consistent commenting style in Javascript try follow a pattern like JSDoc. In JSdoc, while commenting a block of code, you can do something like this:
/***
*
* #param param1
*/
var aFunction = function(param1) {
};
Readable, clean and no magic tric. It is great since it also embeds some sort of static type checking (I know JS is dynamically typed). Also check out this video from one of the guys behind integrating JSDoc into Intellij & Webstorm: Dmitry Jemerov: Static types in JavaScript: what, how and why

Whats happening? One day its OK, the next day its 'undefined'?

I am writing a greasemonkey script. Recently i had this same problem twice and i have no idea why is this happening.
function colli(){
.....
var oPriorityMass = bynID('massadderPriority');//my own document.getElementById() function
var aPriorities = [];
if (oPriorityMass) {
for (var cEntry=0; cEntry < oPriorityMass.childNodes.length; cEntry++) {
var sCollNumber = oPriorityMass.childNodes[cEntry].getAttribute('coll');
if (bynID('adder' + sCollNumber + '_check').checked)
aPriorities.push(parseInt(sCollNumber));
}
}
.....
}
So the mystery of this is, one day i had oPriorityMass named as oPririoty. It was working fine, but the whole function was not yet complete and i started working on another functions for my script. These functions have no connection with each other.
Few days later i decided to go back to my function in the above example and finish it. I ran a test on it without modifying anything and got an error in the firefox's (4) javascript error console saying that oPriority.chilNodes[cEntry] is undefined. NOTE, few days back i have tested it exactly the same way and there was no such problem at all.
Ok, so, i decided to rename oPriority to oPriorityMass. Magically, problem got solved.
At first i thought, maybe there was some conflict of 2 objects, with the same name being used in different functions, which somehow continued to live even outside of function scope. My script is currently over 6000 lines big, but i did a search and found out that oPriority was not mentioned anywhere else but in this exact function.
Can somebody tell me, how and why is this happening? I mentioned same thing happened twice now and they happened in different functions, but the same problem node.childNodes[c] is undefined yet node is not null and node.childNodes.length show correct child count.
What is going on? How do i avoid such problems?
Thank you
EDIT: The error given by error console is
Error: uncaught exception: TypeError: oPriorityMass.childNodes[cEntry] is undefined
In response to Brocks comment:
GM_log(oPriorityMass.childNodes[cEntry]) returns undefined as a message. So node.childNodes[c] is the thing that is undefined in general.
My script creates a div window. Later, the above function uses elements in this div. Elements do have unique IDs and i am 100% sure the original site don't know about them.
My script has a start/stop button to run one or the other function when i need to.
I have been refreshing the page and running my script function now. I have noticed that sometimes (but not always) script will fail with the described error on the first run, however, if i run it again (without refreshing the page) it starts working.
The page has a javascript that modifies it. It changes some of it's element widths so it changes when the browser is resized. But i know it has no effect on my div as it is left unchanged when i resize browser.
EDIT2:
function bynID(sID) {
return top.document.getElementById(ns(sID));
}
function ns(sText) {
return g_sScriptName + '_' + sText;
}
ns function just adds the script name in front of the ID. I use it when creating HTML element so my elements never have the same id as the web page. So bynID() is simple function that saves some typing time when i need to get element by ID.
I have modified my colli() function to include check
if (oPriorityMass) {
if (!oPriorityMass.childNodes[0]) {
GM_log('Retrying');
setTimeout(loadPage,2000);
return;
}
for (var cEntry=0; cEntry < oPriorityMass.childNodes.length; cEntry++) {
var sCollNumber = oPriorityMass.childNodes[cEntry].getAttribute('coll');
if (bynID('adder' + sCollNumber + '_check').checked)
aPriorities.push(parseInt(sCollNumber));
}
}
The loadPage function does 1 AJAX call, then i run few XPATH queries on it, but the actual contents are never appended/shown on the page, just kept inside document.createElement('div'), then this function calls colli(). So now, as i have modified my function, i checked the error console and saw that it may take up to 5 tries for it to start working correctly. 5 x 2seconds, thats 10 seconds. It is never 5 retries always, may vary There's got to be something else going on?
In Firefox, childNodes can include #text nodes. You should check to make sure that childNodes[cEntry] has nodeType == 1 or has a getAttribute method before trying to call it. e.g.
<div id="d0">
</div>
<div id="d1"></div>
In the above in Firefox and similar browsers (i.e. based on Gecko and WebKit based browsers like Safari), d0 has one child node, a text node, and d1 has no child nodes.
So I would do something like:
var sCollNumber, el0, el1;
if (oPriorityMass) {
for (var cEntry=0; cEntry < oPriorityMass.childNodes.length; cEntry++) {
el0 = oPriorityMass.childNodes[cEntry];
// Make sure have an HTMLElement that will
// have a getAttribute method
if (el0.nodeType == 1) {
sCollNumber = el0.getAttribute('coll');
el1 = bynID('adder' + sCollNumber + '_check');
// Make sure el1 is not falsey before attempting to
// access properties
if (el1 && el1.checked)
// Never call parseInt on strings without a radix
// Or use some other method to convert to Number
aPriorities.push(parseInt(sCollNumber, 10));
}
}
Given that sCollNumber seems like it is a string integer (just guessing but it seems likely), you can also use:
Number(sCollNumber)
or
+sCollNumber
whichever suits and is more maintainable.
So, according to your last edit, it now works, with the delay, right?
But when I suggested the delay it was not meant to do (even more?) ajax calls while waiting!!
NOT:
if (!oPriorityMass.childNodes[0]) {
GM_log('Retrying');
setTimeout(loadPage,2000);
return;
More like:
setTimeout (colli, 2000);
So the ajax and the other stuff that loadPage does could explain the excessive delay.
The random behavior could be caused by:
return top.document.getElementById(ns(sID));
This will cause erratic behavior if any frames or iframes are present, and you do not block operation on frames. (If you do block such operation then top is redundant and unnecessary.)
GM does not operate correctly in such cases -- depending on what the script does -- often seeming to "switch" from top scope to frame scope or vice versa.
So, it's probably best to change that to:
return document.getElementById (ns (sID) );
And make sure you have:
if (window.top != window.self) //-- Don't run on frames or iframes
return;
as the top lines of code.
Beyond that, it's near impossible to see the problem, because of insufficient information.
Either boil the problem into a Complete, Self Contained, Recipe for duplicating the failure.
OR, post or link to the Complete, Unedited, Script.

This javascript works in every browser EXCEPT for internet explorer!

The webpage is here:
http://develop.macmee.com/testdev/
I'm talking about when you click the ? on the left, it is supposed to open up a box with more content in it. It does that in every browser except IE!
function question()
{
$('.rulesMiddle').load('faq.php?faq=rules_main',function(){//load page into .rulesMiddle
var rulesa = document.getElementById('rulesMiddle').innerHTML;
var rules = rulesa.split('<div class="blockbody">');//split to chop off the top above rules
var rulesT = rules[1].split('<form class="block');//split to chop off below rules
rulesT[0] = rulesT[0].replace('class=','vbclass');//get rid of those nasty vbulletin defined classes
document.getElementById('rulesMiddle').innerHTML = rulesT[0];//readd the content back into the DIV
$('.rulesMain').slideToggle();//display the DIV
$('.rulesMain').center();//center DIV
$('.rulesMain').css('top','20px');//align with top
});
}
IE converts innerHTML contents into upper case, so you probably are not able to split the string this way, as string operations are case sensitive. Check what the contents really looks like by running
alert(rulesa);
Andris is right. And that's not all. It'll also throw away the quotes in attributes.
It is completely unreliable to make any assumptions about the format of the string you get from innerHTML; the browser may output it in a variety of forms — some of which, in IE's case, are not even valid HTML. The chances of you getting back the same string that was originally parsed are very low.
In general: HTML-string-hacking is a shonky waste of time. Modify HTML elements using their node objects instead. You seem to be using jQuery, so you've got loads of utility functions to help you.
In any case you should not be loading the whole HTML page into #rulesMiddle. It includes a load of scripts and stylesheets and other header nonsense that can't go in there. jQuery allows you to pick which part of the document to insert; you seem to just want the first .blockbody element, so pick that:
$('#rulesMiddle').load('faq.php?faq=rules_main .blockbody:first', function(){
$('#rulesMiddle .blockrow').attr('class', '');
$('.rulesMain').slideToggle();
$('.rulesMain').css('top', '20px');
});
My IE debugger throws an error on your script when I click that button. On this line:
var rulesT = rules[1].split('<form class="block');//split to chop off below rules
IE stops processing the Javascript and says '1' is null or not an object
Don't know if you solve it, but it work's on my Ugly IE ... (its an v8)
Btw: It's me, or does pop-up widows wen open are really, really, really slowing down that platform ?

Escaping dilemma in Javascript

I have the following
var id='123';
newDiv.innerHTML = "";
Which renders in my HTML.
The problem I have is that I wish to take the call to the method TestFunction, and use as a string parameter in my function StepTwo(string, boolean), which would ideally end up in live HTML as shown...
notice how the TestFunction is a string here (it is executed within StepTwo using eval).
I have tried to format my JS as by :
newDiv.innerHTML = "";
but while this appears to me correct in my IDE, in the rendered HTML, it as garbelled beyond belief.
Would appreciate if anyone could point me in the right direction. Thanks!
One of the biggest capital failures on the internet is creating html in javascript by gluing strings together.
var mya = document.createElement("a");
mya.href="#";
mya.onclick = function(){
StepTwo(function(){
TestFunction('123', false );
}, true );
};
newDiv.innerHTML = "";
newDiv.appendChild(mya);
This Eliminates the need for any fancy escaping stuff.
( I probably should do 'onclick' differently, but this should work, I'm trying hard not to just use jQuery code to do everything )
Heres how I would do it in jQuery:
jQuery(function($){
var container = $("#container");
var link = document.createElement("a"); /* faster than $("<a></a>"); */
$(link).attr("href", "Something ( or # )" );
$(link).click( function(){
var doStepTwo = function()
{
TestFunction('123', true );
};
StepTwo( doStepTwo, false ); /* StepTwo -> doStepTwo -> TestFunction() */
});
container.append(link);
});
There is no good excuse for gluing strings together in Javascript
All it does is ADD overhead of html parsing back into dom structures, and ADD potential for XSS based broken HTML. Even beloved google get this wrong in some of their advertising scripts and have caused epic failures in many cases I have seen ( and they don't want to know about it )
I don't understand Javascript is the only excuse, and it's NOT a good one.
Try using " instead of \"
newDiv.innerHTML = "<a href="#"...
You should be using " not " or \" inside an HTML string quoted with double-quotes.
NewDiv.innerHTML = "";
There's probably a better way to do this - any time you find yourself using eval() you should stand back and look for a different solution.
You claim that eval is the right thing to do here. I'm not so sure.
Have you considered this approach:
and in your StepTwo function
function StepTwo(func,args,flag){
//do what ever you do with the flag
//instead of eval use the function.apply to call the function.
func.apply(args);
}
You could create the a element and attach to the click event using DOM Methods.
A Javascript Framework (like the ubiquitous jQuery) would make this a lot easier.
Your biggest problem is using eval, it leads to so many potential problems that it's nearly always better to find an alternative solution.
Your immediate problem is that what you really have is
as the next " after the start of the onclick attribute, closes it. Use " as others have suggested. And don't use eval.
You need to alternate your " and '.
Maybe you don't need quotes around the 123, because of Javascripts flexible typing. Pass it without quotes but treat it as a string within TestFunction.
Hey guys, thanks for all the answers. I find that the quot; seems to work best.
I'll give you guys some votes up once I get more reputation!
In regards to eval(), what you see in the question is a very small snapshot of the application being developed. I understand the woes of eval, however, this is one of those one in a million situations where it's the correct choice for the situation at hand.
It would be understood better if you could see what these functions do (have given them very generic names for stackoverflow).
Thanks again!
The best way is to create the element with document.createElement, but if you're not willing to, I guess you could do or use ".
In your code:
newDiv.innerHTML = "";
If it doesn't work, try changing "\'" to "\\'".
Remember that the " character is used to open and close the attribute on HTML tags. If you use it in the attribute's value, the browser will understand it as the close char.
Example:
<input type="text" value="foo"bar"> will end up being <input type="text" value="foo">.
...
I know this is hella' old now, but if anyone has issues with escaped strings when using eval (and you absolutely have to use eval), I've got a way to avoid problems.
var html = '';
eval('(function(div, html){div.innerHTML = html;})')(newDiv, html);
So, what's going on here?
eval creates a function that contains two parameters, div and html and returns it.
The function is immediately run with the parameters to the right of the eval function. This is basically like an IIFE.
In this case
var myNewMethod = eval('(function(div, html){div.innerHTML = html;})');
is basically the same as:
var myNewMethod = function(div, html){div.innerHTML = html;}
and then we're just doing this:
myNewMethod(newDiv, html); //where html had the string containing markup
I would suggest not using eval. If it can't be avoided, or if you control all the inputs and there's no risk of injection then this will help in cases where string escapes are an issue.
I also tend to use Function, but it isn't any more secure.
Here's the snippet I use:
var feval = function(code) {
return (new Function(code))();
}

Categories