Strange error when appending elements in JavaScript - javascript

I've been getting a problem when trying to append an element in JavaScript, the error I've been getting looks a bit like this:
Uncaught TypeError: Failed to execute appendChild on Node: parameter 1 is not of type Node.
I'm also using using a framework called Interact.js just so you know
here's the peice of code that the browser isn't happy about:
var tempText = [];
var classNum = event.relatedTarget.getElementsByClassName('text');
var newCont = document.createElement('div');
for(var i = 0; i < classNum.length; i++){
tempText.push(event.relatedTarget.getElementsByClassName('text')[i].textContent);
}
for(var i = 0; i < tempText.length; i++){
var pText = document.createElement('p').appendChild(tempText);
newCont.appendChild(pText[i]);
}
var placement = document.getElementById('toolbar')[0];
placement.appendChild(newCont);

I just noticed a small mistake. The document.getElementById returns only a single object. So don't use the [0]:
var placement = document.getElementById('toolbar'); // There is no [0]. Remove it.
placement.appendChild(newCont);
But the whole thing is really easy to do using jQuery. Since you are fine with using a jQuery solution, read on. Please include the jQuery library by adding this piece:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
And the JavaScript would be:
var tempText = [];
var classNum = event.relatedTarget.getElementsByClassName('text');
var newCont = document.createElement('div');
for (var i = 0; i < classNum.length; i++) {
tempText.push(event.relatedTarget.getElementsByClassName('text')[i].textContent);
}
for (var i = 0; i < tempText.length; i++) {
// Change here:
var pText = $("<p />", {html: tempText});
$(newCont).append(pText);
}
var placement = $('#toolbar');
placement.append(newCont);
Since I am unaware of the HTML underlying, I just guessed it and converted a few to jQuery.

As you tagged your question with jquery, you can condense your code to this:
var $newCont = $('<div>');
$('.text', event.relatedTarget).each(function() {
$newCont.append($('<p>').append($(this).text()));
})
$('#toolbar').append($newCont);
Or in a functional programming way:
$('#toolbar').append($('<div>').append(
$('.text', event.relatedTarget).map(function() {
return $('<p>').append($(this).text());
}).get()
));

Related

Can I concat a string and a variable to select a DOM element in JavaScript?

I have tried Googling this question but no luck. Probably because I'm asking the wrong way. Any help is much appreciated.
I have variables copy1, copy2, etc. I want to iterate through them and select each one to check if it's contents has a certain number of characters. When I use any variation of the below, it will either console an error or output a string in the console.
var copy1 = document.getElementById('copy1');
var copy2 = document.getElementById('copy2');
var copy3 = document.getElementById('copy3');
for(var i=0;i<4;i++){
console.log(copy+i);
console.log("copy"+i);
};
Ideally I would be able to select an element and style that via javascript.
Much appreciated
Thanks All.
Moe
Agree with #jaromanda-x:
var copy1 = document.getElementById('copy1');
var copy2 = document.getElementById('copy2');
var copy3 = document.getElementById('copy3');
for (var i=1; i<4; i++) {
console.log(window['copy'+i]);
};
Or you can use more simple example, like:
for (var i=1; i<4; i++) {
var name = 'copy' + i;
console.log(document.getElementById(name));
};
Or even:
for (var i=1; i<4; i++) {
console.log(document.getElementById('copy' + i));
};
You can store the properties in an object where values are set to the DOM element
let copies = {
1 : document.getElementById('copy1'),
2 : document.getElementById('copy2'),
3 : document.getElementById('copy3')
}
for (let [key, prop] of Object.entries(copies)) {
console.log(key, prop)
}
console.log(copies[1], copies[2], copies[3]);
Or use attribute begins with and attribute ends with selectors with .querySelector()
let n = 1;
let copy1 = document.querySelector(`[id^=copy][id$='${n}']`); // `#copy1`
let copy2 = document.querySelector(`[id^=copy][id$='${++n}']`); // `#copy2`
for (let i = 1; i < 4; i++) {
console.log(document.querySelector("[id^=copy][id$=" + i + "]"));
}
Since nobody has addressed your "certain number of characters" requirement yet, I thought I would.
You could always use jQuery or write your own $ method, which works as a document.getElementById() wrapper function.
Here is a jsfiddle to see it in action.
HTML
<div id="copy1">01234</div>
<div id="copy2">012345678</div>
<div id="copy3">0123456789 0123456789</div>
JavaScript
// Isn't this just a brilliant short-cut?
function $(id) {
return document.getElementById(id);
}
for (let i=1; i<4; i++){
let obj = $('copy' + i);
let value = (obj) ? obj.innerText : '';
console.log('value.length:', value.length);
};

How to build-in array sub-function in my setAttribute function?

I have written a function that automatically inserts an attribute (i.e. a per case adapted onclick function) in input elements. It also makes a few exceptions. It looks like this, somewhat simplified for clarity reasons:
function insertAttribute() {
var allInputs = document.getElementsByTagName('input');
var allInputsCount = allInputs.length;
var thatInput = null;
for (i = 0; i < allInputsCount; i++) {
thatInput = allInputs[i];
var highlightFunction = "highlightItem('"+thatInput.name+"-row','"+thatInput.name+"-button')";
if ((thatInput.name != "A") && (thatInput.name != "B") && (thatInput.name != "C"))
thatInput.setAttribute("onclick",highlightFunction);
}
}
The problem is, there are some 20 exceptions. I could expand the if line, but I would rather do it with an array. But how do I do that? I googled how to use array in javascript function, and the (two top) results suggest I should do it like this:
function insertAttribute() {
var allInputs = document.getElementsByTagName('input');
var allInputsCount = allInputs.length;
var thatInput = null;
for (i = 0; i < allInputsCount; i++) {
thatInput = allInputs[i];
var highlightFunction = "highlightItem('"+thatInput.name+"-row','"+thatInput.name+"-button')";
var exceptedArray = ["A","B","C"];
if (thatInput.name != exceptedArray)
thatInput.setAttribute("onclick",highlightFunction);
}
}
But that doesn't work -- the attribute is still inserted in the exceptions. How should it be done? I would need a vanilla script solution. I'll be happy with a good tutorial, too. As you might have guessed, this is the first time I'm using such an array sub-function.
The solution offered in the comment, exceptedArray.indexOf(thatInput.name)==-1 worked in most browsers, but not in IE8. Its script debugger said that it did not support indexOf. It does in other contexts, but apparently not in this context.
In the meantime, I learned how to make a script loop through an array myself. And this works in all browsers:
var allInputs = document.getElementsByTagName('input');
var allInputsCount = allInputs.length;
var thatInput = null;
for (var i=0; i<allInputsCount; i++) {
thatInput = allInputs[i];
var highlightFunction = "highlightItem('"+thatInput.name+"-row','"+thatInput.name+"-button')";
var exceptedNamesArray = ["A","B","C","A4dTInput","A4eTInput"];
var excNamesArrayCount = exceptedNamesArray.length;
var excName = null;
for (var j=0; j<excNamesArrayCount; j++) {
excName = exceptedNamesArray[j];
if (thatInput.name != excName)
thatInput.setAttribute("onclick",highlightFunction);
}
}

create names for automatically created vars

In JavaScript I have a for snippet to create new input elements
for(var g = 0; g < psi.length; g++) {
var newtextLink+(g+1)= document.createElement('input');
//continue with setting attributes
}
I want to put together the word newtextLink and the var g to have something like newtextLink2 everytime for is executed...How can I achieve that?
This is where you usually want an array instead:
var newtextLinks = [];
for(var g = 0; g < psi.length; g++)
{
newtextLinks[g] = document.createElement('input');
}
Then use them via index variables like that (newtextLink[g], newtextLink[0], etc.).
Alternately, there can be places (probably not here) where you really do want names. You can do that with an object:
var newtextLinks = {};
for(var g = 0; g < psi.length; g++)
{
newtextLinks["name" + (g+1)] = document.createElement('input');
}
Now you have newtextLinks.name1, newtextLinks.name2, and so on.
But for this purpose, an array seems best.
If you insist on using the variables, you can do it using the window object:
window['newtextLink' + (g+1)] = document.createElement('input');
Otherwise use an array:
var newTextLinks = [];
...
newTextLinks[g] = document.createElement('input');
Try
this['newtextLink' + (g + 1)] = ...;
function createVariables(){
var newtextLink= [];
for (var i = 0; i <= psi.length; ++i) {
newtextLink[i] = document.createElement('input');
}
return newtextLink;
}
you have newtextLink[0] ... newtextLink[n]
you have similar question here:
JavaScript: Dynamically Creating Variables for Loops

populate select element based on json

How could I populate a second select element? I've figured out how to do the first one. But how could I do the same for the second depending on which "Make" is selected? I've tried to talk myself through it while taking small steps but I'm thinking this may be too advanced for me.
var cars = '{"USED":[{"name":"Acura","value":"20001","models":[{"name":"CL","value":"20773"},{"name":"ILX","value":"47843"},{"name":"ILX Hybrid","value":"48964"},{"name":"Integra","value":"21266"},{"name":"Legend","value":"21380"},{"name":"MDX","value":"21422"},{"name":"NSX","value":"21685"},{"name":"RDX","value":"21831"},{"name":"RL","value":"21782"},{"name":"RSX","value":"21784"},{"name":"SLX","value":"21879"},{"name":"TL","value":"22237"},{"name":"TSX","value":"22248"},{"name":"Vigor","value":"22362"},{"name":"ZDX","value":"32888"}]},{"name":"Alfa Romeo","value":"20047","models":[{"name":"164","value":"20325"},{"name":"8c Competizione","value":"34963"},{"name":"Spider","value":"22172"}]}';
var carobj = eval ("(" + cars + ")");
var select = document.getElementsByTagName('select')[0];
//print array elements out
for (var i = 0; i < carobj.USED.length; i++) {
var d = carobj.USED[i];
select.options.add(new Option(d.name, i))
};
If I read your question right, you want to populate a second select with the models for the make in the first select. See below for a purely JS approach (with jsfiddle). If possible, I would recommend looking into jQuery, since I would prefer a jQuery solution.
http://jsfiddle.net/m5U8r/1/
var carobj;
window.onload = function () {
var cars = '{"USED":[{"name":"Acura","value":"20001","models":[{"name":"CL","value":"20773"},{"name":"ILX","value":"47843"},{"name":"ILX Hybrid","value":"48964"},{"name":"Integra","value":"21266"},{"name":"Legend","value":"21380"},{"name":"MDX","value":"21422"},{"name":"NSX","value":"21685"},{"name":"RDX","value":"21831"},{"name":"RL","value":"21782"},{"name":"RSX","value":"21784"},{"name":"SLX","value":"21879"},{"name":"TL","value":"22237"},{"name":"TSX","value":"22248"},{"name":"Vigor","value":"22362"},{"name":"ZDX","value":"32888"}]},{"name":"Alfa Romeo","value":"20047","models":[{"name":"164","value":"20325"},{"name":"8c Competizione","value":"34963"}, {"name":"Spider","value":"22172"}]}]}';
carobj = eval ("(" + cars + ")");
var makes = document.getElementById('make');
for (var i = 0; i < carobj.USED.length; i++) {
var d = carobj.USED[i];
makes.options.add(new Option(d.name, i));
}
makes.onchange = getModels;
getModels();
}
// add models based on make
function getModels () {
var makes = document.getElementById('make');
var make = makes.options[makes.selectedIndex].text;
for (var i = 0; i < carobj.USED.length; i++) {
if (carobj.USED[i].name == make) {
var models = document.getElementById('model');
models.options.length = 0;
for (var j= 0; j < carobj.USED[i].models.length; j++) {
var model = carobj.USED[i].models[j];
models.options.add(new Option(model.name, j));
}
break;
}
}
}
I would also recommend looking into safer JSON parsing. There is a security risk in using eval if it runs on any user input. You could look into JSON.org and their json2.js. Or if you want to use jQuery: parseJSON. Below is the jQuery version:
jQuery.parseJSON(jsonString);
JSON parsing tips from: Safely turning a JSON string into an object.

How to get DOM live collections with jQuery?

How do I get access to live DOM collections from jQuery?
Take for example this HTML <div id='a'></div> and this JavaScript code:
var a = $('#a');
var divs = a[0].getElementsByTagName('DIV');
for(var i=0; divs.length < 20; ) {
a.append($('<div>'+(i++)+'</div>'));
}
It will append 20 div children to <div id='a'> because divs is a live collection.
Is there anything in jQuery that I could replace the second line with to get the same result?
var divs = $('#a div'); results in infinite loop.
JSFiddle is here.
In case #a already contains divs:
var $a = $('#a'),
toAdd = Math.max(20 - $a.find('div').length, 0);
for (var i = 0; i < toAdd; i++) {
$a.append('<div>' + i + '</div>');
}
That would be equivalent to the code above.
Live Collections - the true ones, are not something which can be returned by modern jquery.
Moreover, modern method which is intended to replace in nearest future getElementsByTagName, getQuerySelectorAll also return a static collection.
This is the answer to question you've stated.
As for the question you've really wanted to ask, other users already tried to provide you some help.
Select the element each time, this will create a new jQuery object. Which I think it the only way if the element is changing.
var a = $('#a');
for(var i=0; $('#a div').length < 20; ) {
a.append($('<div>'+(i++)+'</div>'));
if(i==50) break;
}
EDIT:
Or this:
for(var i=0, a=$('#a'); a.children('div').length < 20; ) {
a.append($('<div>'+(i++)+'</div>'));
if(i==50) break;
}
Or this, just one selector:
var a = $('#a');
var length = a.children('div').length;
while(length < 20) {
a.append($('<div>'+(length++)+'</div>'));
}
How to get DOM live collections with jQuery?
That’s not possible.
This has the same effect as your example code, though:
var $a = $('#a');
for (var i = 0; i < 20; i++) {
$a.append('<div>' + i + '</div>');
}
http://jsfiddle.net/mathias/Af538/
Update: If the code needs to be repeated periodically, you could use something like this:
var $a = $('#a'),
toAdd = Math.max(20 - $('div', $a).length, 0),
i;
for (i = 0; i < toAdd; i++) {
$a.append('<div>' + i + '</div>');
}
http://jsfiddle.net/mathias/S5C6n/
Is it always 20 div children ?
Isn't it better to use the following
var a = $('#a div');
for(var i=0; i < 20; i++) {
a.append($('<div>'+(i)+'</div>'));
}
The syntax you're looking for is:
var divs = $('div#a');
Since IDs are supposed to be unique, you could just do:
var divs = $('#a');

Categories