I have a list of driver names, that is displayed on my page and I want to get more information about the driver when I click on the name. I am struggling with running a function.
Here's what my html looks like:
<main>
<nav id="list">
</nav>
<section id="profile">
</section>
That's my list:
const drivers = [{
name: "data",
age: "data1",
nationality: "data2"
}]
And that's the function I am trying to run:
function driverProfile(drivers) {
const article = document.createElement('article');
const span = document.createElement('span');
const h2 = document.createElement('h2');
h2.textContent = drivers[driver_id].nationality;
span.textContent = drivers[driver_id].age;
article.textContent = drivers[driver_id].name;
}
myProfile = driverProfile(drivers)
profile.appendChild(myProfile)
I get this error Uncaught TypeError: Failed to execute 'appendChild' on 'Node': parameter 1 is not of type 'Node'. pointing to profile.appendChild(myProfile)
My list function:
drivers.forEach(addLink);
function addLink(driver, index) {
const a = document.createElement('a');
a.href = `?driver=${index}`;
a.textContent = driver.name;
list.appendChild(a);
}
It is better practice to group elements. First append elements in div then append new divs to section:
function driverProfile(drivers) {
const div = document.createElement("div");
const article = document.createElement('article');
const span = document.createElement('span');
const h2 = document.createElement('h2');
h2.textContent = drivers[driver_id].nationality;
span.textContent = drivers[driver_id].age;
article.textContent = drivers[driver_id].name;
div.appendChild(h2);
div.appendChild(span);
div.appendChild(article);
return div;
}
myProfile = driverProfile(drivers);
profile.appendChild(myProfile);
The program give error because your function is not of return type and you are assigning function to myProfile variable.
appendChild function takes html elements as Object but you had given function. Just change function to return type as shown in above code.
driverProfile is not returning any node. At the end of this function you need to return the node you want to be appended.
Related
I have an array of objects that I want to display in a template. I wrote the function below and its almost working except when I click on an li, the properties of the object remain the same and don't change if I click on a different li; this is the error that I get : // uncaught TypeError: Cannot read properties of null (reading 'content') at HTMLUListElement.
Here is the code I am using :
function afficherDetailsPokemon() {
const listPokemons = document.getElementById('list-pokemons');
listPokemons.addEventListener('click', event => {
if (event.target.tagName === 'LI') {
const pokemonId = event.target.dataset.pokemonId;
const pokemon = pokemons.find(p => p.id === Number(pokemonId));
if (pokemon) {
// Clone the template
const template = document.getElementById('template').content.cloneNode(true);
// Set the values of the form elements to the properties of the Pokemon object
template.querySelector('#nom_en').value = pokemon.name.english;
template.querySelector('#nom_fr').value = pokemon.name.french;
template.querySelector('#attaque').value = pokemon.base.Attack;
template.querySelector('#defense').value = pokemon.base.Defense;
// Create a new span element for each type and append it to the type_p element
pokemon.type.forEach(type => {
const span = document.createElement('span');
span.textContent = type;
template.querySelector('#type_p').appendChild(span);
});
// Replace the info div with the template
const infoDiv = document.getElementById('info');
infoDiv.parentNode.replaceChild(template, infoDiv);
}
}
});
}
I am pretty new to js and would appreciate your help.
When I click on an li, it does display the properties of the name I select first, but when I try a second time I get the error // uncaught TypeError: Cannot read properties of null (reading 'content') at HTMLUListElement.
If it helps, this happens on the line where I clone the template.
.then(function renderTree (data){
Object.entries(data).map((item, index) => {
let container = document.querySelector(".container")
let li = document.createElement("li")
li.id = `${item[1].id}`
container.appendChild(li)
let span = document.createElement("span")
span.innerHTML = `${item[1].name}`
li.appendChild(span)
let ul = document.createElement("ul")
ul.innerHTML = renderTree(item[1].children)
li.appendChild(ul)
// gerarLi(item[1].id, item[1].name)
})
})
I trying to continue the childs inside a <ul>, the continuation is in:
ul.innerHTML = renderTree(item[1].children)
But this code doesn't work, my index.html return like:
<ul>undefined</ul>
What can I do for this?
I made up my own testing data and played around with your code a bit. These were the points I found:
Your recursive function renderTree() always placed elements directly into the .container div. This should only happen for the outer call.
You did not return anything from your renderTree() function.
I changed the function such that it now returns an HTML-string to be placed inside a <ul> structure. This function can now safely be called recursively.
const data={abc:{id:"first",name:"number one"}, def:{id:"second",name:"el segundo"},
ghi:{id:"third",name:"der dritte",
children:{jkl:{id:"fourth",name:"child one"},mno:{id:"fifth",name:"child two"}}}};
function renderTree(data){ // returns an HTML string for a <UL> DOM element
return Object.values(data).map(({id,name,children}) => {
let li = document.createElement("li");
li.id = id;
let span = document.createElement("span");
span.innerHTML = name;
li.appendChild(span);
let ul = document.createElement("ul");
if (children){
ul.innerHTML = renderTree(children);
li.appendChild(ul);
}
return li.outerHTML;
}).join("\n");
}
document.querySelector(".container").innerHTML=renderTree(data);
span {color:blue}
<ul class="container"></ul>
I also changed the Object.entries() to Object.values() since you never use the keys of the processed objects.
I have created a card structure based on results array fetched from API as:
results holds the list of fetched data
resultsDiv is an empty div to append all cards in it
created cards as :
results.forEach((pred, idx) => {
const card = document.createElement('div')
card.classList.add('card');
card.id = idx;
const image = document.createElement('img');
image.src = pred["image_path"]
image.classList.add('image-style')
card.append(image)
const info = document.createElement('h4')
info.innerText = 'Tenant ID: ' + pred["tenant_id"]
card.append(info)
resultsDiv.append(card)
const ol = document.createElement('ol')
pred['prediction'].forEach(p => {
const li = document.createElement('li')
const checkbox = document.createElement("INPUT");
checkbox.setAttribute("type", "checkbox");
li.innerText = p
li.appendChild(checkbox)
ol.appendChild(li)
})
card.append(ol)
const btn = document.createElement('button')
btn.innerText = 'Verify'
btn.classList.add('verify-btn')
const textarea = document.createElement('textarea')
textarea.placeholder = 'comments...'
card.append(btn)
card.append(textarea)
})
Now I want to get all the data that are selected on respective card on clicking its own button (each button i have added an event listener)
currently I am getting the values using closest this works but i think its not the appropriate way :
btn.addEventListener('click', () => {
const cardDiv = btn.closest('div');
const allElems = cardDiv.childNodes;
const imagePath = allElems[0].src; // image src
const comments = allElems[4].value; //
})
May I know any efficient way of getting the values as above , any guiding links or a solution would be much helpful
TIA
I used an ID to add a new element to divTwo. I want to know how to add the p tag inside divOne and divTwo without referring to their identifier. please help.
// create Tag
const createElement = (elementName) => document.createElement(elementName);
const appendTo = (idElement, element) => document.getElementById(idElement).append(element);
const setAttribute = (eleName, attribute, valueAttribute) => eleName.setAttribute(attribute, valueAttribute);
const setText = (id, text) => document.getElementById(id).innerHTML = text;
// Tag HTML
// div one
const divOne = createElement("div");
const appendDivOne = appendTo("demo", divOne)
const setIDOne = setAttribute(divOne, "id", "divOne");
// div two
const divTwo = createElement("div");
const appendDivTwo = appendTo("demo", divTwo)
const setIDTwo = setAttribute(divTwo, "id", "divTwo");
// child div two
const divTwoChild = createElement("p");
const appendDivTwoChild = appendTo("divTwo", divTwoChild);
const setIDChildeTwo = setAttribute(divTwoChild, "id", "ChildeTwo");
const text = setText("ChildeTwo", "childe two");
<div id="demo"></div>
It seems you are trying to append to div1 which, according to your code, will be the first element in . If you want to append a P tag, you can do:
const divOneChild = createElement("p")
const appendP = appendTo(document.getElementById("demo").firstChild, divOneChild)
You can access your elements directly after creation... for example when using const divTwoChild = createElement("p");, you can use divTwoChild.append() ... there is also a function called insertAdjacentHTML(), where you can add a html code directly on given position, read about in in here. Examples below (last 3 lines):
// create Tag
const createElement = (elementName) => document.createElement(elementName);
const appendTo = (idElement, element) => document.getElementById(idElement).append(element);
const setAttribute = (eleName, attribute, valueAttribute) => eleName.setAttribute(attribute, valueAttribute);
const setText = (id, text) => document.getElementById(id).innerHTML = text;
// Tag HTML
// div one
const divOne = createElement("div");
const appendDivOne = appendTo("demo", divOne)
const setIDOne = setAttribute(divOne, "id", "divOne");
// div two
const divTwo = createElement("div");
const appendDivTwo = appendTo("demo", divTwo)
const setIDTwo = setAttribute(divTwo, "id", "divTwo");
// child div two
const divTwoChild = createElement("p");
const appendDivTwoChild = appendTo("divTwo", divTwoChild);
const setIDChildeTwo = setAttribute(divTwoChild, "id", "ChildeTwo");
divTwoChild.append("childe two"); // <-- here
divOne.append('I am div one!'); // <-- or here
divTwo.insertAdjacentHTML('beforeend', '<p>I am a new p in div 2!</p>'); // <-- or here
<div id="demo"></div>
I was just playing around and went to CNN article and ran this code in the console. The idea was to be able to click a p element and it would replace the DOM node with a node that has the reverse text in it. It works as expected but only once. When I try to run it again to reverse it back, I am hit with the error in console and I can't tell why:
TaggedEventTracker.js:56 Uncaught TypeError: Cannot read property 'dataset' of null
at e.findNearestZjsOrAElement (TaggedEventTracker.js:56)
at e.findNearestZjsOrAElement (TaggedEventTracker.js:63)
at e.trackTaggedEvent (TaggedEventTracker.js:73)
at HTMLDocument.<anonymous> (TaggedEventTracker.js:17)
CODE:
let paragraphs = document.querySelectorAll('p');
paragraphs.forEach(para => {
para.onclick = function(evt) {
updateText(evt.target)
}
})
function updateText(target) {
let node = target;
let nodeText = target.innerHTML;
let newEl = document.createElement('p');
newEl.innerHTML = [...nodeText].reverse().join("");
node.parentNode.replaceChild(newEl, node);
}
Why are you creating a new node to replace the old one? You can just replace the innerHTML of the original element.
When you replace the element, it looks like their event tracker got broken.
Also, by replacing the node, you also replace the event handler associated with it.
let paragraphs = document.querySelectorAll('p');
paragraphs.forEach(para => {
para.onclick = function(evt) {
updateText(evt.target)
}
})
function updateText(target) {
target.innerHTML = [...target.innerHTML].reverse().join("");
}
<p>`123456789</p>
<p>`123456789</p>
<p>`123456789</p>
If you want to keep the original functionality and still replace the text, you will want to clone the node and attach a new event handler.
let paragraphs = document.querySelectorAll('p');
paragraphs.forEach(para => {
para.onclick = function(evt) {
updateText(evt.target)
}
})
function updateText(target) {
let node = target;
let nodeText = target.innerHTML;
let newEl = target.cloneNode();
newEl.innerHTML = [...nodeText].reverse().join("");
node.parentNode.replaceChild(newEl, node);
newEl.onclick = function(evt) {updateText(evt.target)}
}
<p>12233</p>
<p>x3332233</p>
This is all you need if you want to swap text in it. Instead of using innerHTML use textContent.
let paragraphs = document.querySelectorAll('p');
paragraphs.forEach(para => {
para.onclick = function(evt) {
updateText(evt.target)
}
})
function updateText(target) {
let node = target;
node.textContent = [...target.textContent].reverse().join("");
}