I have the following JavaScript that I'd like to translate to CoffeeScript:
function initPage() {
var tr = document.getElementsByTagName('tr')[0];
labs.forEach(function(lab) {
var td = document.createElement('td');
// Create a header for each lab.
var h2 = document.createElement('h2');
h2.innerHTML = lab.name;
td.appendChild(h2);
// Create a div for each machine in a given lab.
for(i = lab.first; i <= lab.last; i++) {
var machine = ((i < 10) ? "0" : "") + i;
var div = document.createElement('div');
div.setAttribute('id', lab.name + "-" + machine);
div.setAttribute('class', 'Grey');
div.innerHTML = machine;
td.appendChild(div);
}
// Append the new table data element to the table row.
tr.appendChild(td);
});
}
Right now my CoffeeScript translation looks something like this:
initPage = () ->
tr = document.getElementsByTagName('tr')[0]
labs.forEach (lab) ->
td = document.createElement 'td'
# Create a header for each lab.
h2 = document.createElement 'h2'
h2.innerHTML = lab.name
tr.appendChild h2
# Create a div for a machine given the machine number
createDiv = (i) ->
machine = if i < 10 then "0#{i}" else "#{i}"
div = document.createElement 'div'
div.setAttribute 'id', "#{lab.name}-#{machine}"
div.setAttribute 'class', 'Grey'
div.innerHTML = machine
td.appendChild div
# Create a div for each machine in a given lab
createDiv machine for machine in [lab.first..lab.last]
# Append the new table data element to the table row.
tr.appendChild td
Is there a better, more idiomatic way to create the divs for each lab? Would it be better to avoid the createDiv function and do something like:
for i in [lab.first..lab.last]
machine = if i < 10 then "0#{i}" else "#{i}"
div = document.createElement 'div'
div.setAttribute 'id', "#{lab.name}-#{machine}"
div.setAttribute 'class', 'Grey'
div.innerHTML = machine
td.appendChild div
The CoffeeScript language reference says
Most of the loops you'll write in CoffeeScript will be comprehensions over arrays, objects, and ranges.
and
Comprehensions should be able to handle most places where you otherwise would use a loop, each/forEach, map, or select/filter
I'm new to the idea of list comprehensions and want to make sure that I'm translating this code in a way that leverages the strengths of CoffeeScript appropriately.
Would it be better to avoid the createDiv function and just inline it?
Yes, that function looks a bit superfluous.
I'm new to the idea of list comprehensions and want to make sure that I'm translating this code in an appropriate way
The aim of list comprehensions is to build new lists, like map and filter would do it. And for which loops or (inappropriately) forEach was used often, manually pushing to an array.
However, your aim is not to create an array, but a DOM element only. Comprehensions don't help here, you will need to use them as loops to execute side effects.
Related
I'm currently working on several Apps that include AJAX calls and I'm using JSON format to retrieve data from the server.
Each page needs to create content based on the JSON response, and I'm currently creating the content like:
function createBox1(json) {
var bigbox = document.createElement('div');
bigbox.className = 'class1';
var firstbox = document.createElement('div');
firstbox.className = 'first-box';
var firstNestedBox = document.createElement('div');
var secondNestedBox = document.createElement('div');
var thirdNestedBox = document.createElement('div');
var secondbox = document.createElement('div');
...
So basically its kinda a long code and I wanted to know if there is a better way to do it.
PS: I have seen some libraries where they do something like:
function o(t,e){var i=document.createElement(t||"div"),o;for(o in e)i[o]=e[o];return i}
and I suppose that's how they create multiple div elements, but I'm not sure how does that works.
Thanks in Advance (:
There's nothing wrong with the way you're doing it, but it's faster to just clone an empty div instead of creating new ones, and it's faster to use a fragment when building markup, and then insert the fragment in the DOM once everything is built instead of inserting each element in the DOM etc.
Just for making shorter code, a function could be used, but it's really just an uneccessary function call:
function createBox1(json) {
var div = document.createElement('div'),
bigbox = div.cloneNode(false),
firstbox = div.cloneNode(false),
firstNestedBox = div.cloneNode(false),
secondNestedBox = div.cloneNode(false),
thirdNestedBox = div.cloneNode(false),
secondbox = div.cloneNode(false):
bigbox.className = 'class1';
firstbox.className = 'first-box';
...
I have a function which creates an Array of components. Each component is an outer div with a few inner divs.
function createDivs(quizQuestions) {
var returnElements = new Array();
$.each(quizQuestions.questions, function(i, val){
// create the div.
quizDiv = $('<div class="questionContainer radius">')
questionDiv = $('<div class="question"><b><span>QuestionText</span></b></div>');
quizDiv.append(questionDiv);
// Now change the question div text.
questionDiv.text = val.question;
answerDiv = $('<div class="answers">');
// ...
// ...
// Now the answers.
questionDiv.append(answerDiv);
returnElements[i] = quizDiv;
});
return returnElements;
I pass JSON such as:
{questions:[{"question":"Name the best Rugby team?",
"answers":["Leinster", "Munster", "Ulster", "Connaught"],
"correct_answer":"Leinster"},
{"question":"Name the best DJ?",
"answers":["Warren K", "Pressure", "Digweed", "Sasha"],
"correct_answer":"Leinster"}]};
I'd like to write a simpe unit test so that I could test the array of div returned made sense
Any tips?
Also, are my better to return a DOM component or just text? The latter would be easier to test.
Thanks.
Not sure exactly what you want to test but it is far more performant to create as much html in strings as you possibly can to reduce function calls. Also append is expensive so ultimately making one string for all the new content represented by the JSON will be the biggest performance gain.
In my opinion it also makes code more readable since fragments are in same order as the would be in html editor
Example(my preferece is creating an array of all the string fragments, concatenation also commonly used):
var newcontent = [];
$.each(quizQuestions.questions, function(i, val) {
newcontent.push('<div class="questionContainer radius">');
newcontent.push('<div class="question"><b><span>' + val.question + '< /span></b > < /div>');
$.each(val.answers, function(idx, answer) {
newcontent.push('<div class="answers">' + answer + '</div > ')
})
newcontent.push(' </div></div > ');
});
Then to add content to DOM:
$('#someDiv').append( newcontent.join(''));
disclaimer: Not fully checked for proper closing/nesting of tags.
How do you use createDocumentFragment to create seven nested div elements in one hit?
I want to create a container A which contains A1, A2, A3 & A4, and then A2a & A2b within A2.
Note: this is a follow-up question to this which explained createDocumentFragment, but not how to nest divs using it. The answer given was as follows (which was helpful as far as it went):
var fragment = document.createDocumentFragment();
function u1(tag, id, className){
var tag = document.createElement(tag);
tag.id = id;
tag.className = className;
fragment.appendChild(tag);
}
// call u1() seven times
document.getElementById('foo').appendChild(fragment);
Could someone explain how to nest? The above just adds seven children to 'foo'. I've trawled the web, but to no avail.
Thanks.
Rather than calling appendChild on the fragment (which creates a top level object in your fragment), you call appendChild on one of the other objects in your fragment and that nests into that object. Here's a pseudo code example that puts tag2 nested into tag.
var tag = document.createElement(tag);
tag.id = id;
tag.className = className;
fragment.appendChild(tag);
var tag2 = document.createElement(tag);
tag2.id = id2;
tag.className = className2;
tag.appendChild(tag2);
Note: you can also set tag.innerHTML and create a whole host of objects (including as many layers of nesting as you want) just from that HTML.
Once I made a recursive function that parsed an JSON object (received from the server) to a DocumentFragment. I need to dig it out again. Here is a JSON of that kind. Recursion start on 'children':
var input="query":"#toPasteId","child":{"#toPasteId":{"a":"div","style":"color:blue","children":[{"a":"span","textcontent":"blabla"},{"a":"div","style":"border: 5px solid red","textcontent":"blublub"}]}}
It might not help but probably you'll find out before I find my paddle.
PS:Found it.
,oParse=function(obj){
var query=obj.query
,curObj=obj.child
,frag=document.createDocumentFragment()
,d=document
,rParse=function(curObj,frag){
var curElem=d.createElement(curObj.a);
frag.appendChild(curElem);
delete curObj.a;
for(var elem in curObj){
switch(elem){
case 'child':
if(curObj.child.length){
for(var i=0;i<curObj.child.length;i++){
rParse(curObj.child[i],curElem);
}
}
else{
rParse(curObj.child,curElem);
}
break;
case 'style':
curElem.style.cssText=curObj[elem];
break;
default:
curElem[elem]=curObj[elem];
}
}
return frag;
};
d.querySelector(query).appendChild(rParse(curObj,frag));
};
oParse(input);
First I'm brand new to JS but have an idea that object classes are what I should be looking into, yet I can't find a straightforward tutorial that doesn't shoot off into arrays,arguments or silly alert boxes! All I'm looking for is something like this:
<head>
<Script Language="JavaScript">
function Class (color, bodypart, item)
Var1 =newClass ('red', 'hand', 'ball')
Var2 =newClass ('green', 'foot, 'bat')
btw I have hundreds of these Vars to write, this is the reason for looking into this method
then put these Vars into some HTML:
('<div><span style="color:'+color+';>'+bodypart+'</span><br/>'+item+'</div><br/>');</script></head>
now in the body I want to call one of the Vars (e.g. Var1) and put it (with the HTML) into a div so it would automatically generate:
<div><span style="color:red;">hand</span><br/>item</div><br/>
My question:what exactly do I put in the head and body to make this happen??
I'm guessing something to do with GetElementById, Classes, etc but I can't find the right syntax to make a simple example work.
Aaaaany help would be fantastically appreciated for such a noob! (p.s. I have spent the last full 2 days reading tutorials and forum posts but they keep losing me in far more complex things than I can grasp)
You sound like you're in over your head at this point, and if you plan on doing a lot of javascript, you're much better off finishing off those tutorials on arrays and such.
Having said that, to help you jump ahead a little, here's what you need to do:
Go to http://www.jquery.com and follow the instructions on how to include jQuery onto your page
Write a javascript function that uses jQery to transform a javascript array into your html
That function might look something like:
var parts = [
['red', 'hand', 'ball'],
['green', 'foot', 'ball']
];
makePart = function(part){
var div = $('<div />');
var span = $('<span />').css({color:part[0]}).text(part[1]);
div.append( span );
div.append( $('<br />') );
div.append( part[2] );
$('body').append( div ).append( $('<br />') );
}
// this next line makes all your parts
$.each( parts, function(index, part) { makePart(part); } );
// while this would only make the first part
makePart( parts[0] );
There are a number of templating libraries around that will turn arrays of data into DOM structures. However, if your requirements are simple then you can put the data into an array and iterate over it to generate HTML or DOM elements:
var data = [
['red', 'hand', 'ball'],
['green', 'foot', 'bat']
];
function genElements(data) {
var frag = document.createDocumentFragment();
var div = document.createElement('div');
var span = document.createElement('span');
var br = document.createElement('br');
var b, d, s, x;
for (var i=0, iLen=data.length; i<iLen; i++) {
x = data[i];
d = frag.appendChild(div.cloneNode());
s = d.appendChild(span.cloneNode());
s.style.color = x[0];
s.appendChild(document.createTextNode(x[1]));
div.appendChild(br.cloneNode());
frag.appendChild(br.cloneNode());
alert(frag.childNodes.length);
}
}
Alternatively you can generate HTML and insert that:
function genElements(data) {
var html = [];
var x;
for (var i=0, iLen=data.length; i<iLen; i++) {
x = data[i];
html.push('<div><span style="color:' + x[0] + '">' +
x[1] + '</span><br>' + x[2] + '</div><br>');
}
return html.join('');
}
Then insert it wherever is appropriate. But you could do exactly the same logic on the server and just send the HTML, avoiding all the issues with client side scripting. Overall it would be much more efficient and robust.
I am currently have a chunk of string which actually a html source code stored in it. What I am trying to do now is to read out specific tags which I require using javascript. Can anyone help me with this, I am new to programming and I am not too sure how to go about it.
The problematic code:
if (request.readyState == 4) {
var html_text = request.responseText;
var parent = document.createElement('div');
parent.innerHTML = html_code;
var metas = parent.getElementsByTagName('meta');
var meta;
for (var i = 0; i < metas.length; i++) {
meta = metas[i];
alert(meta.property);
alert(meta.content);
}
}
The meta content works, but just that the meta property returned are undefined.
Use the DOM (Document Object Model) API. The Mozilla Dev Network (née Mozilla Dev Center) is a great starting point an all-around reference.
JavaScript Guide
The DOM and JavaScript
Traversing an HTML table with JavaScript and DOM Interfaces
What I am trying to do now is to read out specific tags which I require using javascript.
var text = /* whatever string that contains HTML */;
First you need to parse the string:
var parent = document.createElement('div');
parent.innerHTML = text;
Then you can search for whatever kind of element you're looking for. Say you're looking for <table> elements.
var tables = parent.getElementsByTagName('table');
Now you can do whatever you need to each element found:
var table;
for (var i=0, len=tables.length; i<len; i++)
{
table = tables[i];
// do something with the element
}
Relevant API docs
document.createElement
element.innerHTML
element.getElementsByTagName
Attributes of XML nodes are not readily available as DOM object properties. Use getAttribute
Sample: http://jsfiddle.net/mendesjuan/6Pdmw/
var node = document.createElement('div');
node.innerHTML = "<meta property='prop1' content='cont1'>"+
"<meta property='prop2' content='cont2'>";
var metas = node.getElementsByTagName('meta');
for (var i = 0; i < metas.length; i++) {
var meta = metas[i];
alert(meta.getAttribute("property"));
alert(meta.getAttribute("content"));
}