wait until insertAdjacentHTML is done then continue code - javascript

I dynamically add content to my web page using insertAdjacentHTML like the following example code:
...
for(let i = 0; i < someArray.length; i++)
{
someDiv.insertAdjacentHTML('beforeend', `<div id='post-${i}'>...</div>`);
let insertedObject = document.getElementById(`post-${i}`);
// at this point `insertedObject` is null!
}
...
but content does not get added instantly and insertedObject is null, now I have tried finding fixes and the most common is to put a setTimeout to wait until the new element is added but thats too slow! could there be a better way to get the newly added element?

Following on from mine and Peter's comments here's how I might approach this.
Create a containing element. Let's assume that you want it to catch click events from the HTML you're about to insert (event delegation). Add a listener to it that, for example, calls a function that logs the id of the clicked post.
Then map over the array to produces an array of HTML strings that you then join up, and then use insertAdjacentHTML to add that HTML to the container.
// Create the container and add a listener to it
const container = document.querySelector('.container');
container.addEventListener('click', handleClick);
const arr = [1, 2, 3, 4];
// `map` over the array to create some HTML strings, joining
// them up when the iteration is complete
const html = arr.map(n => {
return `<div class="post" id="post-${n}">${n}</div>`;
}).join('');
// Add the HTML to the container
container.insertAdjacentHTML('beforeend', html);
// When the container catches an event fired from
// a child element, check that it's an element with
// a post class, and then log its id
function handleClick(e) {
if (e.target.matches('.post')) {
console.log(e.target.id);
}
}
.container { width: 50%; }
.post { background-color: lightgray; margin: 0.25em; padding: 0.25em; }
.post:hover { background-color: yellow; cursor: pointer; }
<div class="container">
</div>

Related

How to automatically map a function to a newly added DOM element

I'm trying to automate calling a function (initBox) on all elements with class box on the page in vanilla javascript. I have made a functional solution except that it only applies to elements that are on the page when it is loaded. If I add an element to the DOM using javascript, the function (initBox) is not called on it.
Of course I can call the function (initBox) manually after adding the element, but I would like to automate it.
I'm looking for something similar to what jQuery does for events.
For example:
$('table').on('click', 'td', function (event) {
doSomething();
});
This event is called even if I add the TD element to the table later via javascript.
Here is my current solution:
function addBox() {
var btn = document.getElementsByTagName('button')[0];
var el = document.createElement('div');
el.classList.add('box');
el.innerText = (document.getElementsByTagName('div').length + 1);
btn.before(el);
}
function initBox(el) {
el.innerText += ' Initialized';
}
document.querySelectorAll('.box').forEach(initBox);
.box {
display: inline-block;
border: 1px solid #1f2227;
padding: 20px;
}
button {
padding: 20px;
}
<div class="box">1</div>
<div class="box">2</div>
<button onclick="addBox()">Add box</button>
The OP's problem can be solved just by the usage of a MutationObserver instance where the OP needs the callback from the list off added nodes just to initialize the very nodes which do match the OP's definition of a box.
function addBox() {
var btn = document.getElementsByTagName('button')[0];
var el = document.createElement('div');
el.classList.add('box');
el.innerText = (document.getElementsByTagName('div').length + 1);
btn.before(el);
}
function initBox(el) {
el.innerText += ' Initialized';
}
document.querySelectorAll('.box').forEach(initBox);
function initializeBoxClassDivOnly(node) {
if (node.nodeType === 1 && node.matches('div.box')) {
initBox(node);
}
}
function handleNodeInsertion(mutationList/*, observer*/) {
for (const mutation of mutationList) {
mutation
.addedNodes
.forEach(initializeBoxClassDivOnly);
}
};
const observer = new MutationObserver(handleNodeInsertion);
observer
.observe(document.body, { childList: true, subtree: true });
.box {
display: inline-block;
border: 1px solid #1f2227;
padding: 20px;
}
button {
padding: 20px;
}
<div class="box">1</div>
<div class="box">2</div>
<button onclick="addBox()">Add box</button>
Look Into Mutation Observers, they are the new way of observing dom objects, it allows you to run a function when an item is added or removed or modified in a particular DOM element, or if you want you can go old school ( meaning you add event listeners).
Mutation Observers ( Recommended Method ) https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
Old School Evens https://developer.mozilla.org/en-US/docs/Web/API/MutationEvent
if you want me to write the code, let me know in the comments.

How to output a specific element value to console.log by clicking a specific element

enter image description here
As shown in the picture above, when the first element value is 7,
If I click, I want to output 7 in console.log.
var container1 = document.createElement('div')
container1.className = 'container1';
document.body.appendChild(container1);
for(var index = 0;index < 20 ; index++){
var aa = document.createElement('span')
aa.innerHTML = card1[index];
container1.appendChild(aa);
aa.className = 'card'+index;
aa.addEventListener('click',clickEvent)
}
function clickEvent(){
//What code should I use?
}
how are you?
Try this:
function clickEvent(evt){
console.log(evt.target.innerHTML)
}
For this you could use the textContent property of HTML elements in JavaScript. To complete this you would also need to pass the event into your clickEvent function. Click events have a event.target, which is the HTML node that was clicked. The event
var container1 = document.createElement('div')
container1.className = 'container1';
document.body.appendChild(container1);
for(var index = 0;index < 20 ; index++){
var aa = document.createElement('span')
aa.innerHTML = card1[index];
container1.appendChild(aa);
aa.className = 'card'+index;
aa.addEventListener('click',clickEvent)
}
function clickEvent(event){
//What code should I use?
let clickedBox = event.target;
console.log(clickedBox.textContent);
}
As far as I know, this should work like you're intending. I haven't worked with the front-end side of JS lately, so correct me if I'm wrong.
You need to capture the click event in your function clickEvent by doing so:
function clickEvent(event){
console.log(event.target.textContent);
}
Capturing the event is done by passing it as an argument while creating your function, do not pass it when you add it to the event listener.
The event is the clicking event, one of its properties is something called target that points to the element that the event click has occurred on, from there you can access anything almost on that element, including its value which I assume you meant its text (the number on it).
If you have provided the HTML and CSS too then it will be a direct answer to your need but a similar approach can be done using this keyword which print innerHTML of each span tag on click
function paret(reed) {
console.log(reed.innerHTML)
}
div {
display: flex;
justify-content: space-between;
}
span {
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
width: 80px;
height: 160px;
background-color: red
}
<div>
<span onclick="paret(this)">7</span>
<span onclick="paret(this)">17</span>
<span onclick="paret(this)">9</span>
<span onclick="paret(this)">8</span>
<span onclick="paret(this)">5</span>
</div>

Using removeChild() properly

I have a function that appends a "div" child into a parent node, then I need to delete this child using the removeChild() method, but it doesn't work.
This is my code:
function ColorCards()
{for (i=0; i<numerocaselle; i++)
{document.getElementsByClassName("MemoryCards")[i].style.border="none"
var esempiocolore=document.createElement('div')
esempiocolore.style="position: relative; height: 80px; width: 130px; background-image: url('img/backcard"+cartaesempio+".png'); background-size: cover;"
document.getElementsByClassName("MemoryCards")[i].appendChild(esempiocolore)
}
}
function CleanColorCards()
{for (i=0; i<numerocaselle; i++)
{document.getElementsByClassName("MemoryCards")[i].style.border="dashed 3px #02A494"
document.getElementsByClassName("MemoryCards")[i].removeChild(document.getElementsByTagName("div"))
}
}
Does somebody have any suggestion on how to make it work?
You are passing an NodeList to removeChild, while you should pass a single node. Secondly, document.getElementsByTagName("div") is going to also find elements that are not children of the parent you are trying to remove a child from.
So do it like this:
// Avoid repetition of code, and store the result in a variable:
var nodelist = document.getElementsByClassName("MemoryCards");
for (var i=0; i<numerocaselle; i++){
const parent = nodelist[i];
parent.style.border="dashed 3px #02A494";
// Perform `getElementsByTagName` on the parent node only, and take first match:
parent.removeChild(parent.getElementsByTagName("div")[0]);
}
Note that querySelector is designed for getting one node-result, so that last line can read:
parent.removeChild(parent.querySelector("div"));
Just a couple notes:
Using a for loop is unnecessary. Having a variable to hold the count of the length of .MemoryCards will leave room for errors. Instead, I recommend an Array function such as .forEach() to iterate through your elements.
The bulk of your element styles should be handled with classes in CSS. By doing this your function will be more concise and easier to manage.
And, to answer your question:
To remove all child nodes for each .MemoryCards, I would recommend using a loop and the node.removeChild() method as it will perform faster than setting node.innerHTML=''.
See the comments in this post as why this method would be best.
let cartaesempio = 10;
ColorCards = () =>
Array.from(document.getElementsByClassName("MemoryCards"))
.forEach(e => {
e.classList.add('borderNone');
let esempiocolore = document.createElement('div');
esempiocolore.style.backgroundImage = `url('https://cdn.dribbble.com/users/285803/screenshots/1066705/dribbble.gif')`;
e.appendChild(esempiocolore);
});
CleanColorCards = () =>
Array.from(document.getElementsByClassName("MemoryCards"))
.forEach(e => {
e.classList.add('boderDashed');
while (e.firstChild) {
e.removeChild(e.firstChild);
}
});
ColorCards();
CleanColorCards();
/* Children of the .MemoryCards nodes */
.MemoryCards div {
position: relative;
height: 80px;
width: 130px;
background-size: cover;
}
.borderNone {
border: none;
}
.boderDashed {
border: dashed 3px #02A494;
}
<div class='MemoryCards'></div>
Hope this helps,
getElementsByTagName returns node list(array). You will have to select a node. Maybe something like this would be useful for you:
document.getElementsByTagName("div")[0]
https://developer.mozilla.org/en-US/docs/Web/API/Element/getElementsByTagName
just check your error ! document.getElementsByTagName("div") return an array of div that's basic js meaning you've to search more by ourself.
Use a manual, like the one at w3schools or a book whatever :-)
You are passing removeChild all the divs in the document.
Try replacing this line:
document.getElementsByClassName("MemoryCards")[i].removeChild(document.getElementsByTagName("div"))
With:
var memoryCardsEl = document.getElementsByClassName("MemoryCards")[i];
memoryCardsEl.removeChild(memoryCardsEl.getElementsByTagName("div")[0]);

How to select a new added element and edit it?

I have an <a> element:
<a id='addNewElementk' onclick='//Some Js Code' class='continueButton'>Click To Add</a>
When this anchor is clicked , A new element added:
New Added Element
And the first anchor which was clicked , Is removed.
I want to select that new element.
I tried:
window.onload = function(){
var newElem = document.getElementsByClassName('continueButton')[1];
alert(newElem.innerHTML);
}
I'm using ('continueButton')[1] , As there is another input with the same class before that anchor.
But for sure I get Click To Add from the first one , As that's was found when the page is loaded.
So how can I select that new element?
You're attempting to select the element before it exists in the DOM.
You instead need to run that code within the click event handler of the first <a>, like this:
window.onload = function() {
document.querySelector('#addNewElementk').addEventListener('click', function(e) {
e.preventDefault();
var a = document.createElement('a');
a.textContent = 'New Added Element';
a.href = '#';
a.classList.add('continueButton');
a.addEventListener('click', function(e) {
console.log(a.innerHTML);
});
this.parentNode.insertBefore(a, this);
this.remove();
});
}
<a id='addNewElementk' href="#" class='continueButton'>Click To Add</a>
Note the use of addEventListener() over the outdated on* event attributes which should be avoided.
You are attempting to select on an element that doesn't exist in the DOM. Dynamically added elements can be accessed in a couple of ways, above someone has an answer that adds an event listener to the created element which is a solid solution. The other most common way would be to use event delegation (if you are familiar with jQuery that would be $(parentElement).on('action', 'elementWeWantToWatch', function)) in Vanilla js the pattern is effectively the same, find or make a container element for your dynamic html, then add a listener to that container. Inside the listener you will want to ensure the target matches whatever your dynamic selection would be and execute when you find a match.
In this Example
The event listener is initiated on page load to watch the container element. The listener watches for clicks on elements with the continueButton class and when it finds one it removes the clicked element and adds a new element (the counter is to demonstrate that new content is being displayed :D)
(function() {
let i = 1;
const makeButton = () => {
const a = document.createElement('a');
a.classList.add('continueButton');
a.href = '#';
a.textContent = `Button ${i}`
i++;
return a;
}
const init = () => {
const container = document.querySelector('.test');
container.addEventListener('click', e => {
if (e.target.classList.contains('continueButton')) {
let button = makeButton();
container.appendChild(button);
container.removeChild(e.target);
return;
}
});
};
if (document.readyState == 'loading') {
window.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})()
.test {
width: 100%;
display: block;
text-align: center;
}
.continueButton {
display: block;
color: white;
background-color: green;
border-radius 2px;
padding: 15px 30px;
line-height: 2;
margin: 50px auto;
width: 200px;
text-decoration: none
}
<section class="test">
<a id='addNewElementk' class='continueButton'>Click To Add</a>
</section>

How do I repeat div classes using JavaScript only?

Okay, I'm unsure how to word the question, but basically I want to repeat my div containers that have a class of "blocks" using only javascript, no HTML (other than the HTML needed to start a page). IF I were doing this using HTML the result should look exactly like this.
http://jsfiddle.net/nqZjB/1/
<div class = "blocks"> <!-- Repeats three times -->
However as I stated in the description I do not want to use any HTML, so here is my fiddle with javascript only.
How do I make div class blocks repeat three times as in my HTML example using only javascript? Of course in real life I would use HTML for this as javascript is unnecessary, but I want to do this in pure javascript so I can learn. Also as a sidenote if you have a better way as to how I should have worded the question, let me know.
Thanks (:
http://jsfiddle.net/TbCYH/1/
It's good you see the use of making a function of a re-occurring pattern.
Before posting it in StackOverflow, have you tried doing it yourself?
jsfiddle: http://jsfiddle.net/kychan/W7Jxu/
// we will use a container to place our blocks.
// fetch the element by id and store it in a variable.
var container = document.getElementById('container');
function block(mClass, html) {
//extra html you want to store.
return '<div class="' + mClass + '">' + html + '</div>';
}
// code that loops and makes the blocks.
// first part: creates var i
// second: condition, if 'i' is still smaller than three, then loop.
// third part: increment i by 1;
for (var i = 0; i < 3; i++) {
// append the result of function 'block()' to the innerHTML
// of the container.
container.innerHTML += block('block', 'data');
}
Edit: JS has changed a lot since the original post. If you do not require compatibility, use const, template literals, class and querySelector to make the code a bit cleaner. The following code has a Builder class and assumes there is a div with ID 'container':
// create class builder.
class Builder {
// create constructor, accept an element selector, i.e #container.
constructor(targetContainerSelector) {
// search element by given selector and store it as a property.
this.targetContainer = document.querySelector(targetContainerSelector);
}
// method to append to innerHtml of target container.
appendUsingInnerHtml(divAsHtml) {
this.targetContainer.innerHTML += divAsHtml;
}
// method to append to target container using DOM elements.
appendUsingDom(divAsDom) {
this.targetContainer.appendChild(divAsDom);
}
}
// constant to hold element selector.
const myTargetContainer = '#container';
// constant to set the class if required.
const myDivClass = 'my-class';
// constant to hold the instantiated Builder object.
const builder = new Builder(myTargetContainer);
// loop 3 times.
for (let i=0; i<3; i++) {
// call method to append to target container using innerHtml.
builder.appendUsingInnerHtml(`<div class="${myDivClass}}">innerhtml div text</div>`);
// OR.. build using DOM objects.
// create the div element.
const div = document.createElement('div');
// create text element, add some text to it and append it to created div.
div.appendChild(document.createTextNode('dom div text'));
// call method to append div DOM object to target container.
builder.appendUsingDom(div);
}
Please note: Every time something is added to the DOM, it forces the browser to reflow the DOM (computation of element's position and geometry).
Adding everything at once, improve speed, efficiency and performance of a code.
(ref: document.createDocumentFragment)
window.onload = Create();
function Create() {
// create the container
var mainContainer = document.createElement('div');
mainContainer.id = 'mainContainer';
// add all style in one go
mainContainer.setAttribute('style', 'witdht: 400px; height: 200px; border: 2px solid green; margin-left: 20px;');
var divBlocks1 = document.createElement('div');
divBlocks1.className = 'blocks';
divBlocks1.setAttribute('style', 'width: 100px; heigth: 100px; border: 1px solid black; margin-left: 20px; margin-top: 10px; floar: left;');
var divBlocks2 = divBlocks1.cloneNode(false); // copy/clone above div
var divBlocks3 = divBlocks1.cloneNode(false); // copy/clone above div
// everything is still in memory
mainContainer.appendChild(divBlocks1);
mainContainer.appendChild(divBlocks2);
mainContainer.appendChild(divBlocks3);
// now we append everything to the document
document.body.appendChild(mainContainer);
}
Good luck
:)
for(var d=0;d<10;d++){
var aDiv = document.createElement('div');
aDiv.className = "block";
document.getElementsByTagName('body')[0].appendChild(aDiv);
}
Rather than creating the elements before hand and then appending them to the main container, consider dynamically creating and appending them in a loop.
http://jsfiddle.net/TbCYH/6/
for(var i = 0; i < 3; i++) {
var divBlock = document.createElement("div");
divBlock.className = "blocks";
mainContainer.appendChild(divBlock);
}
In the above code snippet a div is being created and appended for each iteration of the loop (which is set to cease at 3).
Also if possible, always use CSS classes rather than modifying the styles for each div directly.

Categories

Resources