Below is a snippet of some of my code. Think a todo list on steriods. I am trying to create multiple Div elements that save to a page that contain a different list every time my "Save button is pressed. whats actually happening is that multiple divs are showing but the original div saved is the only one that gets updated with the list information(so like in a todo list when you click submit a new list item appears ive added a save button and an input field that the user can use to name their list and saves that list to a container but only one div gets updated). I know i'm almost there but ive been looking at this for a couple of hours now and cant quite figure it out. https://github.com/W33K5Y/TODO-PIRPLE
const saveButton = document.getElementById("submit-save");
const myLists = document.getElementById("my-lists");
const startNote = document.getElementById("start-note");
const listName = document.getElementById("new-list-name");
const myUl = document.getElementById("my-ul-lists");
// ! savebutton listener
saveButton.addEventListener("click", addNewTodo);
// ! make new html elements
const newTodoOl = document.createElement("ol");
const newTodoLi = document.createElement("li");
const listH1 = document.createElement("h4");
// ! =============== function for creating new todo ================================
function addNewTodo() {
const todoDiv = document.querySelector(".todo-container");
const todos = document.querySelectorAll(".todo-item");
todos.forEach(function(todo) {
createLi(todo);
});
listName.value ? listH1.innerText = listName.value : listH1.innerText = "My List";
newTodoDivWrap.classList.add("new-todo-div");
newTodoDivWrap.appendChild(listH1);
newTodoDivWrap.appendChild(newTodoOl);
myLists.appendChild(newTodoDivWrap);
todoReset(todoDiv, startNote);
startLoginSignUpNoneLobbyFlex();
}
// todo function to go in above that removes all of whats in the tido-container
function todoReset(div, lobbyDiv) {
lobbyDiv.remove();
div.firstElementChild.innerHTML = "";
}
function createLi(todo) {
// ! Create LI
const newTodo = document.createElement('li');
newTodo.innerText = todo.innerText;
newTodo.classList.add("todo-saved-item");
newTodoOl.appendChild(newTodo);
}
I think the following is why it's not working as you intended:
const newTodoOl = document.createElement("ol");
const newTodoLi = document.createElement("li");
const listH1 = document.createElement("h4");
Remember that javascript creates references, so when you do something like this—newTodoDivWrap.appendChild(listH1)—you don't add a new element, you only add a reference to said element.
It's the same as if you had two objects.
var a = {'name': 'Anette'}
var bbbb = a // creates a reference, not a new object.
bbbb.name = 'Bjorn'
console.log(a.name) // Bjorn
So create new elements inside the method, instead of creating and calling public ones.
Also, comments like this are so unnecessary:
function createLi(todo) {
// ! Create LI
You had a method name that perfectly explains what it does. You don't need to comment that. Start making it a habit of naming variables or method to explain what's going on—you're already doing that (ex. startLoginSignUpNoneLobbyFlex)—so you don't have to use comments. Comments are useless, unless it's for documentation.
You have to move
const newTodoOl = document.createElement("ol");
const newTodoLi = document.createElement("li");
const listH1 = document.createElement("h4");
into the addNewTodo function. That way each iteration produces a brand new List
Rickard pointed me in the right direction :)
Related
I'm making a moviefilter website for school. Now the last part is that I need to link the imdbID from my array to the movie posters on the screen. I made the links, the filter mechanics work but when I click on a poster all the ID's of that filter are being added to the end. Not just one.
My teacher says I can forEach trough the movieArray array and write everything I need in that loop. Can someone help me or look at my code what I'm doing wrong. I wrote 2 seperate functions now to create the arguments needed.
My code:
const addMoviesToDom = function (movieArray) {
const movieList = document.querySelector("#movielist");
movieList.innerHTML = "";
const moviePoster = movieArray.map(item => {
return item.Poster;
});
const imdbId = movieArray.map(item => {
return item.imdbID;
})
moviePoster.forEach(element => {
let newLi = document.createElement("li");
let newLink = document.createElement("a");
let images = document.createElement("img");
images.src = element;
newLink.setAttribute("href", "https://www.imdb.com/title/" + imdbId);
newLink.setAttribute("target", "_blank");
newLi.append(newLink);
newLink.append(images);
movieList.appendChild(newLi);
})
};
addMoviesToDom(movies);
Thanks in advance, hopefully you guys understand what I'm trying to explain, this is all pretty new for me.
imdbId is an array of all the IDs. When you concatenate that to "https://www.imdb.com/title/" it's turned into a string, which joins all the IDs with a , delimiter. So you're not setting the href to just the ID corresponding to the current moviePoster element.
You need to index the array to get the correct ID. forEach provides the array index as the second argument to the callback function, so you can change element => to (element, i) =>, and then use imdbId[i].
But a simpler way would be to skip creating the moviePoster and imdbId arrays, and just create all the elements from movieArray.
const addMoviesToDom = function(movieArray) {
const movieList = document.querySelector("#movielist");
movieList.innerHTML = "";
movieArray.forEach(element => {
let newLi = document.createElement("li");
let newLink = document.createElement("a");
let images = document.createElement("img");
images.src = element.Poster;
newLink.setAttribute("href", "https://www.imdb.com/title/" + element.imdbId);
newLink.setAttribute("target", "_blank");
newLi.append(newLink);
newLink.append(images);
movieList.appendChild(newLi);
})
};
addMoviesToDom(movies);
I have a simple forEach loop in which I'm trying to append list items to an unordered list. However, when I run the script, I'm only seeing one list item being added. Can anyone explain why this is happening?
JS
let bookElement = document.getElementsByClassName("book");
let navElement = document.getElementsByTagName("nav")[0];
let unorderedList = document.createElement("UL");
let listItems = document.createElement("LI");
let book1 = new Book();
let book2 = new Book();
let booksArray = [book1, book2];
createNavigation(booksArray, navElement, unorderedList, listItems);
function createNavigation(booksArray, navElement, unorderedList, listItems){
console.log(booksArray)
booksArray.forEach(function(book){
unorderedList.appendChild(listItems);
})
navElement.appendChild(unorderedList);
}
HTML
<body>
<nav></nav>
<div class="book"></div>
</body>
The log in the function is returning that there are two objects in the array.
You only ever create one list item.
You then append it in multiple places.
Since an element can't exist in multiple places at the same time, each time you append it (after the first) you move it.
Use let listItems = document.createElement("LI"); inside your loop.
Yes, there are two objects in the array, but you're adding the same element to the unorganised list twice. So, the second time it's added, the browser sees that the element is already there and doesn't add it again.
A better approach would be to simply create a new li element in each iteration of the loop:
function createNavigation(booksArray, navElement, unorderedList){
console.log(booksArray)
booksArray.forEach(function(book){
let listItems = document.createElement("LI");
unorderedList.appendChild(listItems);
})
navElement.appendChild(unorderedList);
}
Notice that I've removed the listItems parameter as the value passed into it is now unused.
You can take your program a step further, now, by doing something along these lines:
function createNavigation(booksArray, navElement, unorderedList){
console.log(booksArray)
booksArray.forEach(function(book){
let listItems = document.createElement("LI");
listItems.innerHTML = book.title;
unorderedList.appendChild(listItems);
})
navElement.appendChild(unorderedList);
}
That will create a list of book titles, assuming you have titles stored in a title property in your Book constructor.
I have managed to create the array with the names that I need. These names are pushed or removed from the array based on user’s clicks on various html elements(buttons).
I am attempting to use the values collected within the array to call changes upon html elements that have class names corresponding/matching the names within the array.
I have managed to create a function that activates a window alert that allows me to see and verify that I am able to cycle through all elements collected within the array. But I got stuck. I couldn’t figure out how to use the individual values/names within the array to call the specific classes of html elements.
I have tried:
for (var a = 0; a < array.length; a++) {
document.getElelemntsByClassName(“.”+array[a]).classList.add(“new”);
//and//
document.querySelectorAll(“.”+array[a]).classList.add(“new”);
//none of them worked. So I wasn’t able to get through to the specific html elements.//
window.alert(“.”+array[a]);
//This responds properly. I can get multiple alerts, one at the time, with all the names I am expecting to see.//
}
Thank you in advance for your help.
I believe you want to use an object instead of an array, since indexes on an array will change as you remove items. That said, you may not even need the object, depending on what you want to do with the element. In the snippet below, I added classNames as an object to treat it as an associative array, for example:
// This is shared between two functions
const LIST_ITEM_SELECTOR = '.js-list-item'
// Get top-level elements
const listElement = document.querySelector('.js-list')
const listItemTemplate = document.querySelector('.js-list-item-template')
const addButton = document.querySelector('.js-add-button')
const logButton = document.querySelector('.js-log-button')
// Replaces "array" from your example
const classNames = {}
// Removes the list item from the list element (also delete from classNames)
const handleDelete = e => {
const parentListItem = e.currentTarget.closest(LIST_ITEM_SELECTOR)
const listItemId = parentListItem.dataset.id
delete classNames[listItemId]
parentListItem.remove()
}
// Updates after each "Add"
let nextId = 0
const handleAdd = () => {
// Build new element from the template
const newListItem = listItemTemplate.content
.cloneNode(true)
.querySelector(LIST_ITEM_SELECTOR)
// Add class to the element and the "classNames" object
const className = `id-${nextId}`
newListItem.classList.add(className)
classNames[nextId] = className
// Add data-id
newListItem.dataset.id = nextId
// Update text
newListItem.querySelector('.js-text').textContent = `Item Text ${nextId}`
// Add delete event listener to the nested x button
newListItem.querySelector('.js-x-button').addEventListener('click', handleDelete)
// Append the newListItem to the end of the list
listElement.appendChild(newListItem)
// Prep the nextId for the next "Add" click
nextId += 1
}
addButton.addEventListener('click', handleAdd)
logButton.addEventListener('click', () => {
console.dir(classNames)
})
<button class="js-add-button">Add</button>
<ul class="js-list"></ul>
<template class="js-list-item-template">
<li class="js-list-item">
<span class="js-text">Item Text</span>
<button class="js-x-button">x</button>
</li>
</template>
<button class="js-log-button">Log Out Data</button>
Project Concept: Creating an "exam maker", which can be accessed by a teacher to create and let a student be able to access it to take. Many features would be included but to keep it simple on the question at hand i wont be including all info.
Front End: List all questions in the database, using a php file, to a select field in HTML. When the item is selected add it to the test. Display the test, and assign scoring to each question.
My Actual Question/Help: My addq() function is supposed to, get the value of selected item, append it on the global testArray=[]; while the for loop iterates through each one to display them individually after each one is added.
The Problem: What mine is displaying in HTML... it keeps adding the arrays so the output is repeated over and over after each addq(). Please help fix it! -- the array needs to be outside the function so I can access it later and send it off to a php file.
<h4><center>Test</center></h4>
<ol id="test">
</ol>
<script>
var testArray= [];
function addq(){
var addingquestion = document.getElementById('questionSelect').value;
var myArray = testArray.push(addingquestion);
var node = document.createElement("LI");
for(i=0;i<20;i++){
var textnode = document.createTextNode(testArray[i].toString());
node.appendChild(textnode);
document.getElementById("test").appendChild(node);
}
}
</script>
Example Output Issue Picture:
enter image description here
the problem is that you're appending the array every time to the node element. So, every time it will output the old values with the new ones
You don't have to make it as an array because it stacks without an array,
you just need to replace this :
var textnode = document.createTextNode(testArray[i].toString());
with this :
var textnode = document.createTextNode(addingquestion);
Here, you need to be creating you LI each time, it is an object.
var testArray = [1,2,3,4,5,6,7,8,9,10];
function addq() {
// REMOVED because not provided.
//let addingquestion = document.getElementById('questionSelect').value;
//let myArray = testArray.push(addingquestion);
let test = document.querySelector('#test');
testArray.forEach(t => {
// create new li for each item.
let li = document.createElement('li');
let textnode = document.createTextNode(t);
li.appendChild(textnode);
test.appendChild(li);
});
}
addq();
<h4>
<center>Test</center>
</h4>
<ol id="test">
</ol>
So my situation is a bit different to issues found online.
I want the node Societies to be updated with a new child node Foot19 when the button is pressed. The context is that it is a list of school groups that when the user presses on the button to 'follow' them, the ID to that group is added to their personal data section.
So when they click addFootballSoc button Foot19 is added as a new child node to their specific Societies node.
The key values in the at the start of each sUsers node is equivalent to their authentication uid which is why I'm calling it using firebase.auth()
document.getElementById('addFootballSoc').onclick = function() {addFootball()};
function addFootball(){
var runUid = firebase.auth().currentUser.uid;
var searchRef = firebase.database().ref('sUsers');
var newSearchRef = searchRef.child(runUid);
var mm = newSearchRef.child('Societies')
mm.child.set('Foot19'){}
}
I want it to add a new child for each group they join so they can't overwrite either, for example if I were to add a 'Arts' group button
EDIT:
changed the function so the onclick works and it does now write to where I want,
however, if I were to add another child node alongside Foot19 it will delete Foot19 and replace it, I would use push but I don't want to have the unique key added to my nodes. How can I change this to ensure more nodes can be added?
document.getElementById('addFootballSoc').onclick = function() {addFootball()};
function addFootball(){
var runUid = firebase.auth().currentUser.uid;
var searchRef = firebase.database().ref('sUsers');
var newSearchRef = searchRef.child(runUid);
var mm = newSearchRef.child('Societies')
mm.child.set('Foot19'){}
}
You may be calling the function immediately addFootball() on page load before you have the uid back/resolved from Firebase or looks like you are handing .set the wrong type of data. Try this.
document.getElementById('addFootballSoc').addEventListener("click", function(evt) {
var runUid = firebase.auth().currentUser.uid;
if(runUid){
var searchRef = firebase.database().ref('sUsers');
var newSearchRef = searchRef.child(runUid);
var mm = newSearchRef.child('Societies')
mm.child.set({'Foot19':true});
} else {
console.log("no uid");
}
});
Solved it.
screengrab
The screen grab shows the entries being placed in the child node societies within the users ùid node. The extra entires ALT5 & ASN11 are where I changed the value on line 6 to test that new nodes would not be overwritten.
function addFootball(){
var runUid = firebase.auth().currentUser.uid;
var searchRef = firebase.database().ref('sUsers');
var newSearchRef = searchRef.child(runUid);
newSearchRef.child('Societies').update({
Foot19 : true
})
}