first of all sorry if my english isn't my native language.
I'm just starting to learn JS and i'm trying to create a basic todolist on codepen (https://codepen.io/Jonathan_Design/pen/OJwqXrO) but i'm stuck cause I don't know how to select an element added with JS (not on the base of the document).
I tried :
`const trash = document.querySelector('.todolist__list__raw__trash');
trash.addEventListener('click', function(){
console.log(this);
})`
but this one only work on the first "raw" cause this one is on the html code base.
I did this but only with Jquery :
$(document).on('click', '.todolist__list__raw__trash', function() { $(this).parent().remove(); });
but I really want to learn it in JS :)
Thx
Jonathan
Does anyone can help me to select a element added with JS ?
querySelector() only selects the first element it finds in the DOM. For more than one element, use querySelectorAll().
Define the trash selector at the top with your others.
Make a function to get the list of trash elements, and assign an event listener to all of them.
Make the todoPlus event listener an anonymous function and call the addRaw() and the new cleanTrash() functions inside of it.
Now each time you add a new item, it checks the DOM for the new trash element, and adds the event listener to it.
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// JAVASCRIPT
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//SELECTEURS
const todoPlus = document.querySelector(".todolist__list__plus");
const todoRaw = document.querySelector(".todolist__list__raw:last-child");
const todoTrash = document.querySelector(".todolist__list__raw:last-child .todolist__list__raw__trash");
const todoList = document.querySelector(".todolist__list");
let trash = document.querySelectorAll('.todolist__list__raw__trash');
//ECOUTEURS
todoPlus.addEventListener("click", () => {
addRaw()
cleanTrash()
});
// const todoList = document.querySelector(".todolist__list");
// todoDelete.addEventListener("click", deleteRaw);
//FONCTIONS
function addRaw(event){
// CREATION ELEMENT + CLASSE
const newTodoDivRaw = document.createElement('div');
const newTodoInputCheck = document.createElement('input');
newTodoInputCheck.setAttribute("type", "checkbox");
const newTodoInputText = document.createElement('input');
newTodoInputText.setAttribute("type", "text");
newTodoInputText.setAttribute("placeholder", "Add a task ...");
newTodoInputText.setAttribute("size", "30");
const newTodoSpanTrash = document.createElement('span');
const iconTrash = document.createElement('i');
iconTrash.className = 'fa fa-trash';
// INSERTION DE LA NOUVELLE LIGNE
newTodoDivRaw.classList.add('todolist__list__raw');
todoList.appendChild(newTodoDivRaw);
newTodoInputCheck.classList.add('todolist__list__raw__checkbox');
newTodoDivRaw.appendChild(newTodoInputCheck);
newTodoInputText.classList.add('todolist__list__raw__text');
newTodoDivRaw.appendChild(newTodoInputText);
newTodoSpanTrash.classList.add('todolist__list__raw__trash');
newTodoDivRaw.appendChild(newTodoSpanTrash);
newTodoSpanTrash.appendChild(iconTrash);
}
// SUPPRESION DE LA RAW
function cleanTrash() {
trash = document.querySelectorAll('.todolist__list__raw__trash');
trash.forEach((t) => {
t.addEventListener('click', function(){
console.log(this);
})
})
}
Related
I am working on an app which fetch the data from API and create a list of the books based on the data received from API. it's an API which gives book titles and information. I generated dynamic li elements and generated a button inside the li. each button has a hidden input element which keep book titles in it. the problem is I'm trying to define a onclick event listener for buttons, since buttons are generated dynamically they don't have id. I want to create an event listener for buttons so that once one specific button is clicked the value of hidden input element that is defined inside the button is passed. I couldn't figure out a way to do that. how to make it to understand which specific button has been clicked so it return the input value that is attached to it.
any help would be really appreciated.
here is a portion of my code.
async function overViewMaker(){
const response = await fetch(api_url_overview.concat(api_key));
let data = await response.json();
data = data.results.lists;
data.forEach(book => {
let mybook = book.books;
mybook.forEach(eachbook => {
var book_div = document.getElementById('book_list');
var liTag = document.createElement("li");
var aTag = document.createElement("buttom");
var inpuHidden = document.createElement("input");
inpuHidden.setAttribute("type", "hidden");
inpuHidden.value = eachbook.title;
aTag.appendChild(inpuHidden);
liTag.appendChild(aTag);
book_div.appendChild(liTag);
});
});
}
Each button is already an element object and so you can use addEventListener directly on the element.
async function overViewMaker() {
const response = await fetch(api_url_overview.concat(api_key));
let data = await response.json();
data = data.results.lists;
// moved book_div out of for loop so it doesn't need to be re-queried for every book
var book_div = document.getElementById("book_list");
data.forEach((book) => {
let mybook = book.books;
mybook.forEach((eachbook) => {
var liTag = document.createElement("li");
var aTag = document.createElement("button");
var inpuHidden = document.createElement("input");
inpuHidden.setAttribute("type", "hidden");
inpuHidden.value = eachbook.title;
aTag.appendChild(inpuHidden);
aTag.addEventListener("click", (ev) => {
// you don't need to get book title from the hidden input element since it is in the scope
// inputHidden.value is also accessible from inside of here
const title = eachBook.title;
console.log(title);
});
liTag.appendChild(aTag);
book_div.appendChild(liTag);
});
});
In order to learn Javascript, I created a little website on local.
I have a menu composed of "li", a empty "div", and several "section".
When a click is done on a "li", the appropriate "section" goes into the empty "div" and its class="hidden" is removed.
To make this, I have a loop on my "li" and when there is a click on one of them, a function swapContent() is called inside the li.addEventListener...
Everything works fine ! But, in one specific "section", I have buttons that also work with a click and addEventListener. And that doesn't work.
When I comment the code inside my script file and test it with the console, it works but not when it's called from the script.
I also tried including the javascript code related to those buttons inside the "li" loop right after where the addEventListener (click) with the swapContent() are called and it works !
window.addEventListener("load", function() {
const frame = document.getElementById('frame');
function swapSection(liText, secId){
let section = document.getElementById(secId);
let clone = section.cloneNode(true);
clone.classList.remove('hidden');
while (frame.firstChild) frame.firstChild.remove();
frame.appendChild(clone);
let h2Select = document.getElementsByClassName('hero');
h2Select[0].innerText = liText;
};
// -- Navigation --
var liList = document.querySelectorAll('[id^="li_"]');
for (var item of liList) {
let li = item;
let liId = li.id;
let liText = li.innerText;
// if <li> has an ID show section onclick
if (liId) {
const reg = /li/i;
let secId = liId.replace(reg, 'sct');
// Display content into frame on item menu's click
li.addEventListener('click', function(){
swapSection(liText, secId);
}, false);
}
};
// Buttons that don't work
const toChange = document.getElementById('toChange');
const btnDisable = document.getElementById('disable');
const btnEnable = document.getElementById('enable');
btnEnable.addEventListener("click", function(e) {
toChange.setAttribute("disabled", false);
toChange.innerText = 'Not Clickable'
}
);
btnDisable.addEventListener("click", function(e) {
toChange.removeAttribute("disabled");
toChange.innerText = "Clickable";
});
});
The code on https://codepen.io/Franz333/pen/vqGLZE
Thank you in advance
I'm trying to remove specific li elements, based off of which one has the x button clicked. Currently I'm having an error
"bZMQWNZvyQeA:42 Uncaught TypeError: Failed to execute 'removeChild' on 'Node': parameter 1 is not of type 'Node'."
I am aware that this could mean that the paramater is null, but this dosn't make any sense to me. Chrome dev tools show that the onClick attribute is correctly exectuing removeItem, and passing in the idName as a parameter. How is this not working?
var note = 0;
function saveInfo() {
var idName = "note" + note;
//assign text from input box to var text, and store in local storage
var input = document.getElementById('input').value;
var text = localStorage.setItem(note, input);
var list = document.createElement("li");
var node = document.createTextNode(input);
var removeBtn = document.createElement("button");
list.setAttribute("id", idName);
removeBtn.setAttribute("onClick", `removeItem(${idName})`);
removeBtn.innerHTML = "X";
list.appendChild(node);
list.appendChild(removeBtn);
document.getElementById("output").appendChild(list);
note += 1;
}
function removeItem(name) {
var parent = document.getElementById("output");
var child = document.getElementById(name);
parent.removeChild(child);
}
In my comment, I suggested that you listen to click event bubbling from the removeBtn. In this case, all you need is to remove the onclick attribute assignment logic from your code, and instead give your removeButton an identifiable property, such as a class. Lets give it a class of delete-button:
var removeBtn = document.createElement("button");
removeBtn.classList.add('delete-button');
removeBtn.type = 'button';
removeBtn.innerHTML = 'X';
Then, you can listen to the click event at the level of #output, which is guaranteed to be present at runtime. When the event is fired, you simply check if the event target has the identifiable property, e.g. the remove-button class in our case:
output.addEventListener('click', function(e) {
// GUARD: Do nothing if click event does not originate from delete button
if (!e.target.matches('.remove-button')) {
return;
}
// Delete parent node
e.target.closest('li').remove();
});
If the click event did not originate from the remove button, we simply return and don't do anything else. Otherwise, we know that the button has been clicked, and we can then use Element.closest(), i.e. .closest('li') to retrieve the closest <li> parent node and delete it.
If you absolutely have to support IE11 (which in turn, does not support Element.closest()), you can also use Node.parentNode to access and delete the <li> element, assuming that your remove button is a direct child of the <li> element:
// Delete parent node
e.target.parentNode.remove();
See proof-of-concept below:
var rows = 10;
var output = document.getElementById('output');
for (var i = 0; i < rows; i++) {
var list = document.createElement('li');
var node = document.createTextNode('Testing. Row #' + i);
var removeBtn = document.createElement("button");
removeBtn.classList.add('remove-button');
removeBtn.type = 'button';
removeBtn.innerHTML = 'X';
list.appendChild(node);
list.appendChild(removeBtn);
output.appendChild(list);
}
output.addEventListener('click', function(e) {
// GUARD: Do nothing if click event does not originate from delete button
if (!e.target.matches('.remove-button')) {
return;
}
e.target.closest('li').remove();
});
<ul id="output"></ul>
The issue is that you have missing quotes around the id that you pass to removeItem:
removeBtn.setAttribute("onClick", `removeItem(${idName})`);
This should be:
removeBtn.setAttribute("onClick", `removeItem('${idName}')`);
Better pattern
It is better practice to bind the click handler without relying on string evaluation of code, and without needing to create dynamic id attribute values:
removeBtn.addEventListener("click", () => removeItem(list));
And then the function removeItem should expect the node itself, not the id:
function removeItem(child) {
child.parentNode.removeChild(child);
}
You can remove the following code:
var idName = "note" + note;
list.setAttribute("id", idName);
Basically, I have a text box where I write some text, click "Add Post", and a new post will be prepended to a list of other posts (similar to Twitter). Each of these posts has a 'delete' button.
For now, when I click delete on each posts, I have a console.log that just says, "TEST". And it works fine. However, after I add a post, meaning, a new element has been prepended, the event listener stops working, even for the existing 'delete' buttons (not just the newly added one).
This is how I wrote the event listener for the delete.
for(var i = 0; i < deleteDOM.length; i++) {
deleteDOM[i].addEventListener("click", deleteEntryCont);
}
...Where deleteEntryCont is just a function that console logs "TEST" for now.
deleteDOM is the variable for document.getElementsByClassName("delete") and is just a node list.
Here's the part where a new 'post' is added:
entryList.forEach(function(entry) {
var entryItemDOM = document.createElement("li");
var entryTextDOM = document.createElement("p");
var metaWrapperDOM = document.createElement("div");
var timeStampDOM = document.createElement("span");
var deleteDOM = document.createElement("span");
// Create entry wrapper & class names
entryItemDOM.className = "entry";
entryItemDOM.className += ` ${entry.mood}-entry`;
entryItemDOM.id = entry.id;
// Insert entry at the top of the stack
domEl.entriesDOM.insertBefore(entryItemDOM, domEl.entriesDOM.firstChild);
entryItemDOM.appendChild(entryTextDOM);
entryTextDOM.innerHTML = entry.text;
entryItemDOM.appendChild(metaWrapperDOM);
metaWrapperDOM.className = "overflow-hidden";
metaWrapperDOM.appendChild(timeStampDOM);
timeStampDOM.className = "timestamp";
timeStampDOM.innerHTML = entry.timeStamp;
metaWrapperDOM.appendChild(deleteDOM);
deleteDOM.className = "delete";
deleteDOM.innerHTML = "Delete";
});
Where entryList is an array of objects that this above code renders in HTML.
What could be the issue?
As discussed in the comments, you're only adding the event listeners when the page is loaded, which means it's only added to the posts that are visible at the time. You need to add the listener separately to each new post you create:
metaWrapperDOM.appendChild(deleteDOM);
deleteDOM.className = "delete";
deleteDOM.innerHTML = "Delete";
deleteDOM.addEventListener('click', deleteEntryCont);
I have a function that creates an html element with an unique ID.
And after that I want that when I click this element I could call a new function.
Quick example:
1) I click a button "Create element";
2) An element is created with id of "New_Element";
3) I click the "New_Element";
4) I get a function that was already preset to this element.
My current code for creating an element.
var pageRows = document.getElementsByClassName('pageRows');
var pageRowID = "section";
var el = document.createElement('section');
el.setAttribute('id', pageRowID + pageRows.length);
var row = document.getElementById('allNewRows');
row.parentNode.appendChild(el);
el.innerText = "New " + pageRows.length + " ROW!";
Now that the Element of id "pageRowId0" is created I want to have a function that works when I click this element.
Best wishes.
Thanks for helping.
You can do element.onclick= function(){}
var pageRows = document.getElementsByClassName('pageRows');
var pageRowID = "section";
var el = document.createElement('section');
el.setAttribute('id', pageRowID + pageRows.length);
el.onclick = function(){
/*write your fn here*/
};
var row = document.getElementById('allNewRows');
row.parentNode.appendChild(el);
el.innerText = "New " + pageRows.length + " ROW!";
You can use event delegation:
var row = document.getElementById('allNewRows');
row.parentNode.onclick = function(e) {
if (e.target.nodeName.toLowerCase() == 'select') {
//click on target select element
}
};
The snippet below has two parts. The first piece of code allows you to add a bunch of elements with different texts to the document.
The second parts shows the text of the element you clicked.
You will notice that the click event handler is just assigned to the parent element in which the new elements are added. No explicit click event handlers are bound to the new element.
I like to use addEventListener, because I think it's better to add a listener for a specific goal than to override any other event listeners by bluntly setting 'onclick', but that's a matter of opinion.
// Only run this code when the DOM is loaded, so we can be sure the proper elements exist.
window.addEventListener('DOMContentLoaded', function(){
// The code to add an element when the add button was clicked.
document.getElementById('add').addEventListener('click', function() {
var element = document.createElement('div');
element.innerText = document.getElementById('text').value;
element.className = 'clickableElement';
document.getElementById('elements').appendChild(element);
});
// Click event handler for the 'elements' div and everything in it.
document.getElementById('elements').addEventListener('click', function(event) {
var target = event.target; // The element that was clicked
// Check if the clicked element is indeed the right one.
if (target.classList.contains('clickableElement')) {
alert(target.innerText);
}
});
})
<input id="text" value="test"><button id="add">add</button>
<div id="elements"></div>