Arg, I’m losing hair.
I’m having trouble understanding something extremely basic and important. I have functions who call functions who call functions… but I’m having trouble doing anything useful with their results. I can’t seem to “grab” them. They just get garbage collected. Scope is becoming my enemy.
This is also hard to explain because the code is modular, so stuff is calling stuff is calling stuff.
Everything happens inside a large Object.
In Object.init, I set event handlers and listeners.
when a form control is changed, the listener “selectlistener” calls a function
selectListener: function(event) {
Object.findValue(this);
//and more
},
findValue() needs to find the values inside the input who just got the change event, and it does for most inputs. But one input needs to grab values from a script.
findValue: function(input) {
var div = input.parentNode;
var p = div.lastChild;
var formKey = input.id,
formVal = input.value.toLowerCase();
...
if (formKey == 'Postcode') {
[b] Object.getPrice(input);
//getPrice() calls Object.ajax() who calls Object.gotPrice()
p.firstChild.nodeValue = the results from getPrice[/b]
}
...stuff for other inputs
},
This is the most recent try at this. Object.getPrice is starting a whole ajax thing which is retrieving a value I need. I want to assign that value to p.firstChild.nodeValue (which no outside function can find cause it’s declared inside the findvalue() function).
If I do
p.firstChild.nodeValue = Object.gotPrice;
then I’m just just renaming gotPrice. I want something more like this
p.firstChild.nodeValue = Object.gotPrice(); in other words, I want p.firstChild.nodeValue to = the results of gotPrice. Except, it can’t actually execute it here. gotPrice gets executed by someone else (who is executed by someone else who is ultimately called by p.firstChild.nodeValue’s function).
If the input selected is “PostCode”, I need to send its value to an ajax snippet, and I do get a value, but I can’t do anything with it. I can’t return it out into the open. This other one works:
[b] setBasiskost: function(input) {
return (input.value <= 1999) ? input.value = 2000 : input.value;
},[/b]
...
findValue: function(input) {
var div = input.parentNode;
var p = div.lastChild;
var formKey = input.id,
formVal = input.value.toLowerCase();
...
if (formKey == 'Basiskosten') {
[b] p.firstChild.nodeValue = Begrafenis.setBasiskost(input);[/b]
}
...
The results of “setBasisKost” are given back to p.firstChild.nodeValue of “Basiskosten”. I tried something similar to Postcode but because ultimately that function didn’t actually return a value (I just can’t get it to), it cannot be assigned. I tried with just one big function instead of the separated ones I have, but I can’t even get the value out of the onreadystatechange() of the ajax call… not without sending it to some other function. So I can keep sending this value to a million other functions but I can’t give it back to the element who’s calling it.
I also tried assigning the returned value to some global variable (by prefacing the variable with the object name and not using “var”) but while that doesn’t work, I would rather not do that anyway. The only one who needs to see this result is PostCode.
When you have functions calling functions calling functions, how do you get the final result back to the original caller?? I’ve been going back and back over closures but they always seem to be doing the opposite of what I’m doing: their assigning the results of inner functions as variables to outer functions.
The whole thing, which has been mutilated in attempts to get this working, and won’t work for anyone because the server the script is on is a dev server:
The parts I’m looking at are in bold. So all the other stuff I think can be ignored but it’s there just in case (oh and IE pukes on the “kosten” object so absolutely nothing works in IE).
var Begrafenis = {
init: function() {
var pintro = document.getElementById('pintro'),
pkost = document.getElementById('pkost'),
divs = document.forms['formKost'].getElementsByTagName('div'),
basiskostDiv = document.getElementById('basiskost'),
basiskostInput = document.getElementById('Basiskosten'),
typekiezenInput = document.getElementById('TypeKiezen'),,
inputs = document.forms['formKost'].elements;
/*onload change form action and change submit text*/
var submit = inputs[inputs.length-1];
submit.setAttribute("aria-describedby", 'TotaalKost');
submit.value = 'Volgende »';
document.forms['formKost'].action = 'berekenpremie_berekenen';
/*onload hide intro text and show basikost element*/
Basis.addClass(pintro, 'hidden');
Basis.addClass(pkost, 'hidden');
Basis.removeClass(basiskostDiv, 'hidden');
/*onload run functions and set event listeners*/
Begrafenis.chooseType(typekiezenInput);
Begrafenis.setBasiskost(basiskostInput);
Begrafenis.createParagraphs(divs);
Begrafenis.loadValues(divs);
Basis.addEventListener(basiskostInput, 'blur', Begrafenis.basiskostListener);
Basis.addEventListener(typekiezenInput, 'change', Begrafenis.chooseListener);
var formSelects = inputs;
for (var s=0, ss=formSelects.length; s<ss; s++) {
var typePattern = /^(input|text|select-one)$/i;
if (typePattern.test(formSelects[s].type)) {
Begrafenis.removeText(formSelects[s]);
Begrafenis.findValue(formSelects[s]);
Begrafenis.getTotal(Begrafenis.values);
[b] Basis.addEventListener(formSelects[s], 'change', Begrafenis.selectListener);[/b]
}
}
},
kosten: {
'BasisKosten': {
'value': {
'2000': 2000
}
},
'TypeKiezen': {
'value': {
'begraven': 'zie onder',
'cremeren': 1500
},
'begraven': {
'show': ['Postal', 'Box']
},
'cremeren': {
'hide': ['Postal', 'Box']
}
},
'Kist': {
'value': {
'geen': 0,
'eenvoudig': 400,
'standaard': 750,
'luxe': 1000
}
},
'Herdenkingsdienst': {
'value': {
'geen': 0,
'rouwcentrum': 250,
'kerk': 500
}
},
'Volgautos' : {
'value': {
'geen': 0,
'een': 200,
'twee': 400,
'drie': 600
}
},
'Koffietafel' : {
'value': {
'geen': 0,
'50': 250,
'100': 500,
'150': 750
}
},
'Rouwbrieven' : {
'value': {
'geen': 0,
'50': 150,
'100': 200,
'150': 450
}
},
'DankBid' : {
'value': {
'geen': 0,
'100': 175,
'200': 250,
'300': 325
}
},
'Advertenties' : {
'value': {
'geen': 0,
'regionaal': 650,
'landelijk': 1000,
'beide': 1650
}
},
'Bloemen' : {
'value': {
'geen': 0,
'eenvoudig': 100,
'standaard': 150,
'luxe': 200
}
},
'Grafsteen' : {
'value': {
'geen': 0,
'staande': 1500,
'liggende': 2000,
'grafmonument': 2500
}
}
},
removeText: function(input) {
if (input.type == 'select-one') {
var options = input.options;
var optL = options.length;
for(var i=0; i<optL; i++) {
if(options[i].text.indexOf(':') != -1) {
var optText = options[i].text.split(':');
options[i].text = optText[0];
}
}
}
},
createParagraphs: function(divs) {
/*prijs header p*/
var priceHeader = document.createElement('p');
Basis.addClass(priceHeader, 'infos');
var pText1 = document.createTextNode('Kosten in €');
priceHeader.appendChild(pText1);
divs[0].parentNode.insertBefore(priceHeader,divs[0]);
/*totaalkost p*/
var lastDiv = divs[divs.length-1];
var totaalP = document.createElement('p');
totaalP.id = 'TotaalKost';
Basis.addClass(totaalP, 'infos');
var pText2 = document.createTextNode(' ');
totaalP.appendChild(pText2);
Basis.insertAfter(totaalP, lastDiv);
},
chooseType: function(input) {
var val = input.value.toLowerCase(),
value;
for (result in Begrafenis.kosten[input.id][val].show) {
value = document.getElementById(Begrafenis.kosten[input.id][val].show[result]);
Basis.removeClass(value, 'none');
}
for (result in Begrafenis.kosten[input.id][val].hide) {
value = document.getElementById(Begrafenis.kosten[input.id][val].hide[result]);
Basis.addClass(value, 'none');
}
Begrafenis.setSelectedValue(value);
},
setSelectedValue: function(value) {
var s = document.getElementById('Kist');
var postcode = document.getElementById('Postcode'),
opt;
//if "cremeren" selected, values for postcode en kist moet 0 zijn*/
if (Basis.hasClass(value, 'none')) {
for (var i=0; i<s.options.length; i++) {
opt = s.options[i];
opt.selected = opt.value == 'geen' ? true : false;
}
postcode.value = '';
}
//anders, "eenvoudig" geselecteerd
else {
for (var i=0; i<s.options.length; i++) {
opt = s.options[i];
opt.selected = opt.value == 'eenvoudig' ? true : false;
}
}
//als er een "p" is, is dit niet "onload" maar na "onchange"...
//dus, update de p value
var p = s.parentNode.lastChild;
if (p.nodeType == 1 && p.nodeName.toLowerCase() == 'p') {
Begrafenis.findValue(s);
}
},
loadValues: function(divs) {
for (var i=0, l=divs.length, p, div; i<l; i++) {
div = divs[i];
p = document.createElement('p');
Basis.addClass(p, 'value');
var pText = document.createTextNode('0');
p.appendChild(pText);
div.appendChild(p);
}
},
setBasiskost: function(input) {
return (input.value <= 1999) ? input.value = 2000 : input.value;
},
[b] getPrice: function(input) {
var pc = input.value;
if(pc.length >= 4) {
var pcnum = pc.replace(/[^\\d]/g, '');
if(pcnum.length >= 4) {
pcnum = pcnum.substr(0,4);
Begrafenis.ajaxZonderX(pcnum); //if the user put in anything useful, do teh Ajax
}
else if(pcnum.length < 4) { //otherwise set stuff to 0
pcnum = 0;
pc = pcnum;
return pc;
}
}
else {
return;
}
},
gotPrice: function(result) {
var postcodePrice;
if(isNaN(result)) {
postcodePrice = 'onbekend';
}
else {
postcodePrice = result;
}
alert(postcodePrice); //this works fine, I can alert anything
return postcodePrice; //does not return the value to anyone
},
ajaxZonderX: function(data) {
var xhr = XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"),
url = '/verzekerjebegrafenis.nl/grafkosten.php?postcode=' + data;
if (xhr !==null) {
xhr.open('GET', url, true);
xhr.send(null);
xhr.onreadystatechange = function() {
if (this.readyState == 4) {
if(this.status == 200 || this.status == 304) {
if (this.responseText) {
Begrafenis.gotPrice(this.responseText);
}
else {
alert('Geen response tekst');
}
}
else {
alert('Error: ' + this.status);
}
}
};
}
},[/b]
[b] findValue: function(input) {
var div = input.parentNode;
var p = div.lastChild;
var formKey = input.id,
formVal = input.value.toLowerCase(),[/b]
values = [];
if (formKey == 'Basiskosten') {
p.firstChild.nodeValue = Begrafenis.setBasiskost(input);
}
[b]if (formKey == 'Postcode') {
Begrafenis.getPrice(input);
//p.firstChild.nodeValue = Begrafenis.gotPrice;
}[/b]
for (result in Begrafenis.kosten) {
if (result == formKey) {
p.firstChild.nodeValue = Begrafenis.kosten[formKey].value[formVal];
}
}
var ps = Basis.getElementsByClass('value');
for (var i=0, l=ps.length; i<l; i++) {
var para = ps[i];
values.push(para.firstChild.nodeValue);
}
Begrafenis.values = values;
},
getTotal: function(values) {
var totaal = document.getElementById('TotaalKost'),
totaalValue = 0;
for (var i=0, l=values.length; i<l; i++) {
var pVal = Begrafenis.values[i];
pVal = parseInt(pVal, 10);
pVal = isNaN(pVal) ? 0 : pVal;
totaalValue += pVal;
}
totaal.firstChild.nodeValue = 'Uw totaal begrafeniskosten: \\u20AC' + totaalValue;
},
[b]selectListener: function(event) {
Begrafenis.findValue(this);
Begrafenis.getTotal(Begrafenis.values);
},[/b]
basiskostListener: function(event) {
Begrafenis.setBasiskost(this);
},
chooseListener: function(event) {
Begrafenis.chooseType(this);
}
};
Basis.begin(Begrafenis.init);
Where do I have to go to understand basic Javascript things like getting values back out of a function? It’s always the same problem I hit every time I use functions to figure out some value. It’s always locked away.
If anyone happens to know of some place where one can practice with these things, that would be nice. Closures for morons? Functions for dummies? Something like that. I’ve got bookmarks of pages explaining functions and values and closures but I can’t seem to take that over to what I want to do with them.