.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.
Related
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("");
}
i move var liElement = document.createElement('li'); out side the function addTodo() to make this function work
function removeTodo(){
liElement.remove()
};
but now i have another problem that is i can't add more than one todo (li)
const input = document.getElementById('input');
const addBtn = document.getElementById('btn');
const todoList = document.getElementById('todoList');
var ulElement = document.createElement('ul');
var liElement = document.createElement('li');
let placeholderValue = '';
// This code is for clear placeholder value
input.addEventListener('focus' , () => {
placeholderValue = input.placeholder;
input.placeholder = '';
});
input.addEventListener('blur' , ()=> {
input.placeholder = placeholderValue;
});
// this function is for add to do to a list
function addTodo(){
todoList.appendChild(ulElement)
ulElement.appendChild(liElement);
liElement.classList.add('liElement')
liElement.innerHTML = input.value;
};
addBtn.addEventListener('click' , addTodo)
// this function is for remove todo from the list
function removeTodo(){
liElement.remove()
};
liElement.addEventListener('contextmenu' , (e) => {
e.preventDefault()
removeTodo()
});
You don't use createTextNode for example :
var t = document.createTextNode(input.value);
liElement.appendChild(t);
...and you have builded a very complex structure. Make it easier for yourself.
And finally
const todoList = document.getElementById('todoList');
var ulElement = document.createElement('ul');
why use to createElements ? you don't need these. You can create them in html file.
I am trying to create a list of links using DOM nodes from the data in the database. Instead of having a link for each DOM element. The whole list only have one link. I want the link to be separate because I want to add value to it so I can identify which link the user clicks.
This is the link in HTML
This is my Javascript code in which I take data from database. I want to put each corresponding in database as the value for the corresponding link in the HTML code.
window.onload = async function outsong() {
var selected = localStorage.getItem("category")
document.getElementById("result").innerHTML = selected;
var result = [];
if(selected == "Popular") {
await db.collection("Song").doc("Popular").collection("songs").get().then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
// doc.data() is never undefined for query doc snapshots
console.log(doc.id, " => ", doc.data());
result.push(doc.data());
});
});
console.log(result.length);
for(a = 0; a < result.length; a++) {
var node = document.createElement("li");
var textnode = document.createTextNode(result[a].song_name);
node.appendChild(textnode);
document.getElementById("songlist").appendChild(node);
var anchor = document.getElementById("songlist");
var att = document.createAttribute("value");
att.value = result[a].song_name;
anchor.setAttributeNode(att);
}
}
In the image, I want to separate the link, not one as the whole.
Within your loop, you'll need to create a new <a> element within each <li>, and add the song link to that a element. Modifying your code:
const songList = document.getElementById("songlist");
for(let a = 0; a < result.length; a++) {
var node = document.createElement("li");
var link = document.createElement('a');
link.setAttribute('href', result[a].song_link);
link.innerText = result[a].song_name;
node.appendChild(link);
songList.appendChild(node);
}
I don't see where you're getting the song_link from, so for the sake of the example, I guess it was in the result[a] object, alongside song_name.
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.
I have a Unordered List wist List Items that are created using the Fetch method. The data is JSON data and needs to be relayed to an HTML document
I want the created list items to have a CSS-background based on the JSON data URL. It looks something like this;
fetch(url).then(function(response){
return response.json();
JSON.stringify(data);
}).then(function(data){
for(var i=0;i<data.length;i++){
var createListItem = document.createElement("li");
createListItem.className = "listItem";
var listItem = document.querySelector(".listItem");
var createHeaderItem = document.createElement("h3");
createHeaderItem.innerHTML = data[i].title;
ul.appendChild(createListItem);
createListItem.appendChild(createHeaderItem);
if (data[i] && data[i].media[0] && data[i].media[0].url) {
listItem.style.backgroundImage = "url('"+""data[i].media[0].url""+"')";
} else {
listItem.style.backgroundImage = "none";
}
}
});
I can't get the list item style background to relay the URL. What is the correct way of grabbing the data.media.url and using it as CSS list-item property?
It looks like you have excessive quotes
Try removing the quote after the first + and before the second +
listItem.style.backgroundImage = "url('"+""data[i].media[0].url""+"')";
To
listItem.style.backgroundImage = "url('"+ data[i].media[0].url +"')";
Edited:
for(var i=0;i<data.length;i++){
var createListItem = document.createElement("li");
createListItem.className = "listItem";
var createHeaderItem = document.createElement("h3");
createHeaderItem.innerHTML = data[i].title;
ul.appendChild(createListItem);
createListItem.appendChild(createHeaderItem);
var listItem = document.querySelector(".listItem");
if (data[i] && data[i].media[0] && data[i].media[0].url) {
listItem.style.backgroundImage = "url('"+ data[i].media[0].url +"')";
} else {
listItem.style.backgroundImage = "none";
}
What has changed?
You never declared
var ul = document.createElement("ul");
I'm going to assume this is global variable and declared outside this scope otherwise you have additional problems that needs addressing.
I've moved the following code (see below) further down the code. You attempted to access a DOM element using document.querySelector() on an element that has not yet been added to the DOM. As a result of this you always got null returned.
var listItem = document.querySelector(".listItem");
This excessive quote I raised earlier still stands.
As I have mentioned in my comments listItem is null. This is due to the fact that createListItem has yet to be added to the DOM and .querySelector(..) checks the current DOM tree when the function is called.
There is no need for listItem to exist since you're manipulating and appending to createListItem. So rather than trying to assign .style.backgroundImage = ".." to listItem add it to createListItem:
if (data[i] && data[i].media[0] && data[i].media[0].url) {
createListItem.style.backgroundImage = "url('"+data[i].media[0].url+"')";
} else {
createListItem.style.backgroundImage = "none";
}
Additionally if .listItem, the CSS class, does not have a default background-image of some sort you can omit the above else clause as it is unnecessary:
if (data[i] && data[i].media[0] && data[i].media[0].url) {
createListItem.style.backgroundImage = "url('"+data[i].media[0].url+"')";
}