I suck at Javascript

Am trying to get my head around YUI (building an interface to a database for the lab). It was all going really well, right up until it broke. I’ve got a main.js script, which I’m loading on every page. Some pages also have page-specific scripts that do more stuff. This is the case for the Upload page, whose HTML is something like:

page.html


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<link rel="stylesheet" href="main.css" />
<script type="text/javascript" src="http://yui.yahooapis.com/2.7.0/build/yuiloader/yuiloader-min.js" ></script>
<script src="main.js" type="text/javascript"></script>
<script src="upload.js" type="text/javascript"></script>
</head>
<body>
<div id="messages">
<span class="message">This is a test</span>
<span class="error"></span>
</div>
<form action="" enctype="multipart/form-data" method="post">
<fieldset>
<legend>Your Data File</legend>
<div class="file label">
<label>Select File</label>
<input name="file" type="file" class="validate valid_required" />
</div>
<div class="select label">
<label for="file_format">File Format</label>
<select name="file_format" id="file_format">
<option value="guess">--- Guess Format ---</option>
<option value="excel">Excel</option>
<option value="text">Text</option>
</select>
</div>
<div class="select label">
<label for="delim">Field Delimiter</label>
<select name="delim" class="validate valid_required_if_file_format_is_text" id="delim">
<option value=""></option>
<option value="comma">Comma</option>
<option value="tab">Tab</option>
<option value="space">Single Space</option>
<option value="whitespace">Any Whitespace</option>
<option value="other">Other</option>
</select>
</div>
<div class="text label">
<label for="delim_char">Delimiter Character</label>
<input name="delim_char" type="text" class="validate valid_required_if_delim_is_other" id="delim_char" />
</div>
<div class="submit">
<input name="submit" type="submit" value="Upload" />
</div>
</fieldset>
</form>
</body>
</html>

In both main.js and upload.js, I’m calling the YUI loader and running an init function to do some setup stuff onDOMReady. These calls are wrapped in anonymous function calls, to which they should be scoped (I think, although my grasp of scoping issues in js is tenuous), so they shouldn’t be treading on one another. The scripts are below.

So, the problem is that the main init function doesn’t run properly on the upload page (although it works fine on other pages). However, if I stick an alert() at the start of that function, it all works fine. As my knowledge of javascript debugging extends to sticking alerts in to see what’s going on, I am now stuck. setTimeout makes it work too, so it obviously just needs to wait until something happens. I just don’t know what. Anyone got any suggestions?

main.js


DEVREG = function(){};
DEVREG.config = {
debug : true,
base_uri : 'http://localhost:3000',
};
DEVREG.validator={
required : function(val){
if(val==""){
return(document.createTextNode('This field is required'));
}
return(false);
},
nospace : function(val){
var re = /\s/;
if(re.test(val)){
return(document.createTextNode('Field should not contain spaces'));
}
return(false);
},
email : function(val){
var re = /^([a-zA-Z0-9_.-])+@([a-zA-Z0-9_.-])+\.([a-zA-Z])+([a-zA-Z])+/;
if(re.test(val)){
return(false);
}else{
return(document.createTextNode('Not a valid email address'));
}
},
word : function(val){
var re = /^\w*$/;
if(re.test(val)){
return(false)
}
return(document.createTextNode('Invalid characters: use only letters, numbers and underscore'));
}
};
(function() {
messages_in = function (){
var anim = new YAHOO.util.Anim(
'messages',
{ opacity: { to: 1 } },
5, YAHOO.util.Easing.easeOut);
anim.animate();
};
messages_out = function(){
var anim = new YAHOO.util.Anim(
'messages',
{ opacity: { to: 0 } },
5, YAHOO.util.Easing.easeOut);
anim.animate();
};
validate = function(ele){
mum = ele.parentNode;
YAHOO.util.Dom.removeClass(mum,'error');
var errors = YAHOO.util.Dom.getChildrenBy(mum,function(o){return(YAHOO.util.Dom.hasClass(o,'error_message'));});
for(var i=0; i<errors.length; i++){
mum.removeChild(errors[i]);
}
var cn = ele.className;
cn = cn.split(' ');
var re = /^valid_(.*)/;
var ok = true;
for(var h=0; h<cn.length; h++){
var valdn = re.exec(cn[h]);
if(valdn){
var call = 'DEVREG.validator.'+ valdn[1]+'(ele.value)';
var error = eval(call);
if(error){
ok = false;
YAHOO.util.Dom.addClass(ele.parentNode,'error');
var node = document.createElement('span');
node.appendChild(error);
YAHOO.util.Dom.addClass(node,'error_message');
ele.parentNode.insertBefore(node,ele.parentNode.firstChild);
}
}
}
return(ok);
}
submit_form = function(form){
var eles = YAHOO.util.Dom.getElementsByClassName('validate');
var ok = true;
for(var i=0; i<eles.length; i++){
var ele = eles[i];
if(validate(ele)==false){
ok=false;
};
}
return(ok);
};
function init(){
/*------------ LOOK HERE! ---------------*/
alert('Comment me out and it breaks');
var eles = YAHOO.util.Dom.getElementsByClassName('validate');
for (var i=0; i<eles.length; i++){
eles[i].onchange = function(){validate(this)};
}
for(var i= 0; i<document.forms.length;i++){
document.forms[i].onsubmit=function(){return(submit_form(this))};
}
messages_out();
}
var loader = new YAHOO.util.YUILoader({
base: "",
require: ["animation","dom", "event"],
loadOptional: false,
combine: true,
filter: "MIN",
allowRollup: true,
onSuccess: function(){
YAHOO.util.Event.onDOMReady(init);
},
});
loader.insert();
})();

upload.js


DEVREG.validator.required_if_file_format_is_text = function(val){
var ff_ele = document.getElementById('file_format');
if(ff_ele.value == 'text' && val==''){
return(document.createTextNode('This field is required for text files'));
}
return(false);
};
DEVREG.validator.required_if_delim_is_other = function(val){
var delim_ele = document.getElementById('delim');
if(delim_ele.value == "other" && val==""){
return(document.createTextNode('This field is required if delimiter is "other"'));
}
return(false);
};
(function() {
function init(){
var ff_ele = document.getElementById('file_format');
var delim_ele = document.getElementById("delim");
var delim_mod = new YAHOO.widget.Module(delim_ele.parentNode);
if (ff_ele.value!="text"){
delim_mod.hide();
}
var delim_char_ele = document.getElementById("delim_char");
var delim_char_mod = new YAHOO.widget.Module(delim_char_ele.parentNode);
if (delim_ele.value !="other"){
delim_char_mod.hide();
}
var ff_ele = document .getElementById("file_format");
YAHOO.util.Event.addListener(ff_ele, 'change', function(){
if(ff_ele.value == 'text'){
delim_mod.show();
}
else{
delim_mod.hide();
delim_char_mod.hide();
document.getElementById('delim').value="";
document.getElementById('delim_char').value="";
}
});
var delim_ele = document .getElementById("delim");
YAHOO.util.Event.addListener(delim_ele, 'change', function(){
if(delim_ele.value == 'other'){
delim_char_mod.show();
}
else{
delim_char_mod.hide();
document.getElementById('delim_char').value="";
}
});
}
var loader = new YAHOO.util.YUILoader({
base: "",
require: ["connection","event", "json","container"],
loadOptional: false,
combine: true,
filter: "MIN",
allowRollup: true,
onSuccess: function(){
YAHOO.util.Event.onDOMReady(init);
},
});
loader.insert();
})();

Advertisements

5 responses to “I suck at Javascript

  1. Now I’m really confused: Wrapping the contents of the main.js init function in a setTimeout with a time of 0 still fixes it. Why????

  2. I’ve not read the code but it sounds like you are calling a method before the page has been loaded/rendered. which is why it works after you set the time out (I think the timeout timer doesn’t start till the page is shown)

    try calling the method in one of the onLoad/onrender events.

    If you are going to be debugging javascript I’d recommend downloading firebug for firefox, you can put breakpoints in your code and if you reload the page it usually stops on the breakpoint.

  3. Glad to help 🙂

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s