what happens with the variable when appended with apprendChild [duplicate] - javascript

Trying to create DOM element "gota" from template. First I create template:
function htmlToElement(html) {
var template = document.createElement('template');
template.innerHTML = html;
return template.content.firstChild;
}
let gota = htmlToElement('<div class="gota"><div class="gota-rastro"><div class="rastro"></div></div><div class="gota-cabeza"></div></div>');
Second, I create collection from CSS class "gotea" and iterate for each element to append template:
function gotear() {
let gotas = document.getElementsByClassName('gotea');
for (let i = 0; i < gotas.length; i++) {
gotas[i].appendChild(gota);
}
}
gotear();
This just add "gota" element to a only one random element of the collection:
How can I add this template to ALL elements in a collection?

You're only creating one element. Then you're using that same element with appendChild multiple times, so you move it from one parent to the next.
You can clone the element with cloneNode(true) and append the clone:
gotas[i].appendChild(gota.cloneNode(true));
Side note: You can use insertAdjacentHTML rather than htmlToElement to insert elements based on that HTML directly:
function gotear() {
let gotas = document.getElementsByClassName('gotea');
for (let i = 0; i < gotas.length; i++) {
gotas[i].insertAdjacentHTML(
"beforeend",
'<div class="gota"><div class="gota-rastro"><div class="rastro"></div></div><div class="gota-cabeza"></div></div>'
);
}
}
gotear();
Granted, that means parsing the HTML repeatedly. But if not useful here, it might be useful elsewhere. (There's also insertAdjacentText.)

Related

How to link multiple iterations of a Javascript object to multiple elements?

I have a page that has to display multiple copies of the same Javascript object simultaneously - they will be manipulated individually later, so each element has to be linked a unique copy of the object.
I have this (very simplified) object:
function Person(index){
this.index = index,
this.name = 'Dave',
this.photo = 'photo.jpg',
// ... other properties ...
this.changeName = function(name){
this.name = name;
}
// ... other methods ...
}
I use document.createElement in a for loop to create multiple divs, each of which should display the default name and photo, plus buttons to run functions that div only. The index is passed to the new object, and the divs are displayed in a container element.
function createPeople(num){
for(let i = 0; i < num; i++){
const individual = document.createElement('DIV');
individual.person = new Person(i);
const index = individual.person.index;
const name = '<p>' + individual.person.name + '</p>';
const button = '<button onclick=changeName()>Change name ' + index + '</button>';
// ... other HTML elements ...
individual.innerHTML = name + button;
document.getElementById('container').appendChild(individual);
}
}
The button shows the correct index, but I don't know how to make it's changeName() function target the specific object for that div, or for any changes in each object to be reflected in the correct div.
Insert the button into the document, make sure you have a reference to it, then use addEventListener on it. Don't use an inline handler.
Also, to avoid undesirable possible unsafe scripts running, only insert static HTML with .innerHTML - for dynamic content from the user, use .textContent.
individual.innerHTML = `
<p></p>
<button>Change name ${i}</button>
`;
individual.querySelector('p').textContent = individual.person.name;
individual.querySelector('button').addEventListener('click', () => {
this.changeName();
});
Assigning an individual instance to a property of a DOM element is weird though - if you need to use it elsewhere, consider making a Map mapping elements to their instances instead.

Creating html elements and appending children using a for loop (or mapping?)

I would like to make a function which takes a string as input, creates several elements, then appends each element to the element previously created, so that the text is displayed as a list item, within a div, within a span. The intent is to eventually make a basic to do list, but for now I'm stuck on the basic steps
I thought that a for loop could be useful for the creation of elements, though I can't figure out how to append what I have previously appended. Here's how I started:
const elementsArray = [
'ol',
'li',
'div',
'span',
];
const makeToDoItem = (toDoItem) => {
for (i = 0; i < elementsArray.length; i++) {
const createElement =
document.createElement(elementsArray[i]);
document.body.appendChild(createElement);
};
};
makeToDoItem("post on stackoverflow");
I understand that
document.body.appendChild(createElement);
is doing what I am telling it to do: create four elements in the body. How can I append them the way I would like to?
Is the .map function better for this? I am having trouble grasping how to apply .map here.
Thanks for your help!
const elementsArray = [
'ol',
'li'
];
const makeToDoItem = (toDoItem) => {
for (i = 0; i < elementsArray.length; i++) {
const createElement =
document.createElement(elementsArray[i]);
if (elementsArray[i] === 'li') {
createElement.textContent = toDoItem;
}
document.body.appendChild(createElement);
};
};
makeToDoItem("post on stackoverflow");
makeToDoItem("another post");
You can do the above but instead you probably want to create the <ol> in HMTL and then append <li> elements inside it.

variable null, even without being called

I'm attempting to use JavaScript to add rows to a table. I created an anonymous function iterate that iterates id names, and that works fine. The problem is somewhere in my class.
class CreateTable{
constructor(text) {
this.text = text
}
makeTableRow(){
let self = this;
let row = document.createElement('tr');
for (let i = 0; i < 1; i++) {
let el = document.createElement('td');
el.setAttribute('id', iterate(i));
row.appendChild(el);
}
let en = document.getElementById('id1');
console.log(en);
en.innerHTML = self.text;
return row;
}
}
I adapted this class from something that already works, with some tweaks. The en variable is returning a typeError:en is null, and I can't figure it out. What I want to do is create two empty td elements with callable ids, and then add text to the first element immediately. What is the actual problem that is going on here?
The document.getElementById() function returns null if there is no element with the specified ID in the DOM. The elements that you create in the loop have not been added to the DOM, they're appended to a tr that isn't in the DOM either (it's just returned from your function without having been appended to anything).
Just add the required text directly at the time you create the element:
makeTableRow(){
let self = this;
let row = document.createElement('tr');
for (let i = 0; i < 1; i++) {
let el = document.createElement('td');
el.setAttribute('id', iterate(i));
if (i === 0) // for first element
el.innerHTML = self.text; // set the content
row.appendChild(el);
}
return row;
}
Incidentally, you say you want to create two td elements, but your loop only runs for one iteration. The for condition should be i < 2 if you want it to run twice.
Is null because doesn't is identifying the "id", that meaning that you are writing your script before the id="id1".
You need to write your script at the end, just before of "body"
.......
<div id="id1">
</div>
.....
<script>
....
</script>
</body>

Dynamically generating lists without document.write?

I'm using JavaScript to create a list of links from a text source. Using document.write(), resets/creates a new page which is not desirable. I do not know the amount of items in the list, so precomposing a list and using innerHTML to set values doesn't seem 'do-able' ? How would one go about generating dynamic lists in JS?
You can create elements dynamically by using document.createElement. It could work for example like this:
var container = document.getElementById('my-list'),
items = [],
addItem;
addItem = function (text) {
var elm = document.createElement('li');
elm.innerHTML = text;
container.appendChild(elm);
items.push(elm);
};
// generating random list
var i = 0,
j = Math.floor(Math.random() * 50);
for (i; i < j; i++) {
addItem('list item ' + i);
}
As Matti Mehtonen stated you can use document.createElement(), in order to create dynamically elements. One note on this is that instead of appending the newly created elements directly into a DOMnode, you should instead create a document fragment and append the elements to it, afterwards you append the fragment to the DOMnode. This way the process will be faster.
var items = ['cars', 'toys', 'food', 'apparel'];
function render(elementId, list) {
//getting the list holder by id
var el = document.getElementById(elementId);
//creating a new document fragment
var frag = new DocumentFragment();
//iterate over the list and create a new node for
//each list item and append it to the fragment
for (var i = 0, l = list.lenght; i < l; i++) {
var item = document.createElement('span');
frag.appendChild(item.innerHTML = list[i]);
}
//finally append the fragment to the list holder
el.appendChild(frag);
}
//rendering
render("my-list-holder", list);

How to correctly use innerHTML to create an element (with possible children) from a html string?

Note: I do NOT want to use any framework.
The goal is just to create a function that will return an element based on an HTML string.
Assume a simple HTML Document like such:
<html>
<head></head>
<body>
</body>
</html>
All functions mentioned are in included the head section and all DOM creation/manipulation is done at the end of the body in a script tag.
I have a function createElement that takes a well formed HTML String as an argument. It goes like this:
function createElement(str)
{
var div = document.createElement('div');
div.innerHTML = str;
return div.childNodes;
}
Now this functions works great when you call it like such:
var e = createElement('<p id="myId" class="myClass">myInnerHTML</p>');
With the minor (possibly HUGE) problem that the element created isn't a 'true' element, it still has a parentNode of 'div'. If anyone knows how to fix that, then that would be awesome.
Now if I call the same function with a more complex string:
var e = createElement('<p id="myId" class="myClass">innerHTML<h2 id="h2ID" class="h2CLASS">Heading2</h2></p>');
It creates TWO children instead of ONE child with another child having another child.Once you do div.innerHTML = str. The innerHTML instead of
`<p id="myId" class="myClass">innerHTML <h2 id="h2ID" class="h2CLASS">Heading2</h2> </p>`
turns to
`<p id="myId" class="myClass">innerHTML</p> <h2 id="h2ID" class="h2CLASS">Heading2</h2>`
Questions:
Can I somehow get an element without a parent node after using .innerHTML?
Can I (in the case of the slightly complex string) get my function to return ONE element with the appropriate child instead of two elements. [It actually returns three, <p.myClass#myId>,<h2.h2CLASS#h2ID>, and another <p>]
This is similar to the answer from palswim, except that it doesn't bother with creating a clone, and uses a while() loop instead, always appending the node at [0].
function createElement( str ) {
var frag = document.createDocumentFragment();
var elem = document.createElement('div');
elem.innerHTML = str;
while (elem.childNodes[0]) {
frag.appendChild(elem.childNodes[0]);
}
return frag;
}
You'd have to attach the new element somewhere. Try using a DocumentFragment object in conjunction with the div you created:
function createElement(str) {
var div = document.createElement('div');
div.innerHTML = str;
var container = document.createDocumentFragment();
for (var i=0; i < div.childNodes.length; i++) {
var node = div.childNodes[i].cloneNode(true);
container.appendChild(node);
}
return container.childNodes;
}
It's more overhead, but it does what you want. Note that DOM elements' .insertAdjacentHTML member function is coming in HTML5.
For that complex string you passed, it isn't valid XHTML syntax - you can't have a block element as a child of <p> (<h2> is a block level element).

Categories

Resources