Create span element in li with Javascript - javascript

I'm working on a to-do list project and when creating a new li I would like it to start with a span containing a "X". I wrote the code below, but instead of a span I get "[object HTMLSpanElement]". Anybody knows how to fix this? Thank you!
var enterItem = document.querySelectorAll("input");
var todoList = document.getElementById("todo-list");
for (var i = 0; i < enterItem.length; i++) {
enterItem[i].addEventListener("keypress", function(key) {
if(key.which === 13){
var newLi = document.createElement("li");
var span = document.createElement("span");
var newItem = this.value;
span.textContent = "X";
newLi.appendChild(document.createTextNode(span + " " + newItem));
todoList.appendChild(newLi);
this.value = "";
}
});
}

You are trying to add an html element in a textNode so it triggers the toString of the element
You need
const todoList = document.getElementById("todo-list");
document.getElementById("inputContainer").addEventListener("keypress", function(e) {
const tgt = e.target;
if (tgt.type === "text" && e.which === 13) {
let newLi = document.createElement("li");
let span = document.createElement("span");
span.classList.add("remove");
let newItem = tgt.value;
span.textContent = "X";
newLi.appendChild(span)
newLi.appendChild(document.createTextNode(" " + newItem));
todoList.appendChild(newLi);
tgt.value = "";
}
});
document.getElementById("todo-list").addEventListener("click", function(e) {
const tgt = e.target;
if (tgt.classList.contains("remove")) {
tgt.closest("li").remove();
}
})
<div id="inputContainer">
<input type="text" />
</div>
<ul id="todo-list"></ul>

Related

Can someone explain to me why I am only able to type in one letter in the text input?

For some reason when I try to do this event, it allows for only one letter in the text input. I tried searching for an answer and couldn't figure it out, can someone help me?
let button = document.getElementById("button");
let input = document.getElementById("userInput");
let ul = document.getElementById("list");
input.addEventListener("keypress", function(e){
if(e.keyCode == 13){
let li = document.createElement("li");
li.appendChild(document.createTextNode(input.value))
ul.appendChild(li)}
input.value = "";
}
);
There is another event, but this one works perfectly when alone and there is no text limit
button.addEventListener("click", function(){
let li = document.createElement("li");
if(input.value.length > 0){
li.appendChild(document.createTextNode(input.value))
ul.appendChild(li)}
input.value = "";
}
);
It happens because everytime you press a key it is setting the input.value to be empty. You may want to move the closing bracket to be after the input.value = "";. Like so:
let button = document.getElementById("button");
let input = document.getElementById("userInput");
let ul = document.getElementById("list");
input.addEventListener("keypress", function(e){
if(e.keyCode == 13){
let li = document.createElement("li");
li.appendChild(document.createTextNode(input.value))
ul.appendChild(li)
input.value = "";
}
}
As others have alluded to, input.value = ""; was emptying your input on each keypress event and you needed to move it inside of your if condition.
It seems like you realized the issue but here is a working snippet using your code:
let button = document.getElementById("button");
let input = document.getElementById("userInput");
let ul = document.getElementById("list");
input.addEventListener("keypress", function(e) {
if (e.keyCode == 13) {
let li = document.createElement("li");
li.appendChild(document.createTextNode(input.value))
ul.appendChild(li)
input.value = "";
}
}
);
button.addEventListener("click", function() {
let li = document.createElement("li");
if (input.value.length > 0) {
li.appendChild(document.createTextNode(input.value))
ul.appendChild(li)
}
input.value = "";
}
);
<button id="button">button</button><input id="userInput"></input><br/>
<ul id="list"></ul>

how to subtract a number on the second button click?

function createElement(){
const input = document.getElementById("input");
const ul = document.getElementById("ul");
const li = document.createElement("li");
const div = document.createElement("div");
div.innerText = input.value;
const btn = document.createElement("button");
btn.innerText = "add 2";
li.appendChild(div);
li.appendChild(btn);
ul.appendChild(li);
btn.onclick = () => {
let num = parseInt(div.innerText);
num = num + 2;
const roundednum = num.toFixed(2);
div.innerText = roundednum;
btnCLicked = true;
}
}
document.getElementById("btn").addEventListener("click", createElement);
<input type="number" id="input">
<ul id="ul"></ul>
<button id="btn">click me</button>
i have this simple code above that creates some elements, and it will add 2 to the number in the div when button inside the <li> is clicked, which works perfectly
now i want to subtract 2 from the number when button is clicked the second time, so first click will add 2, second click will subtract 2
so i come up with this code which uses a boolean to add and subtract the number which works
let btnCLicked = false;
function createElement(){
const input = document.getElementById("input");
const ul = document.getElementById("ul");
const li = document.createElement("li");
const div = document.createElement("div");
div.innerText = input.value;
const btn = document.createElement("button");
btn.innerText = "add 2";
li.appendChild(div);
li.appendChild(btn);
ul.appendChild(li);
btn.onclick = () => {
if (btnCLicked === false) {
let num = parseInt(div.innerText);
num = num + 2;
const roundednum = num.toFixed(2);
div.innerText = roundednum;
btnCLicked = true;
}else if (btnCLicked === true) {
let num = parseInt(div.innerText);
num = num - 2;
const roundednum = num.toFixed(2);
div.innerText = roundednum;
btnCLicked = false;
}
}
}
document.getElementById("btn").addEventListener("click", createElement);
<input type="number" id="input">
<ul id="ul"></ul>
<button id="btn">click me</button>
but now the problem is that when i create an <li>, and click the button inside to add 2 to the number, btnClicked is now set to true.
so when i create another element and click the button for the first time it will subtract the number because btnClicked is true, which is what i dont want, i want the first click to add and subtract on the second click
how do i solve that problem? im thinking about something like making the boolean only inside each li so each li has an own boolean and it will not effect other elements but idk lol i have no other idea
Move the let btnCLicked = false; into the function. Each invocation of createElement() will create a [closure`]1 since it's child function (click handler) is using variables from parents scope:
const input = document.getElementById("input");
const ul = document.getElementById("ul");
function createElement() {
let btnCLicked = false;
const li = document.createElement("li");
const div = document.createElement("div");
const btn = document.createElement("button");
div.innerText = input.value || 0;
btn.innerText = "add 2";
li.appendChild(div);
li.appendChild(btn);
ul.appendChild(li);
btn.onclick = () => {
let num = Number(div.innerText);
if (btnCLicked === false) {
num += 2;
} else {
num -= 2;
}
div.innerText = num;
btnCLicked = !btnCLicked;
}
}
document.getElementById("btn").addEventListener("click", createElement);
<input type="number" id="input">
<ul id="ul"></ul>
<button id="btn">click me</button>
Another solution: use event delegation and use a data-attribute to track the last action (add or subtract):
document.addEventListener("click", handle);
function createElement() {
document.querySelector("#ul").insertAdjacentHTML("beforeend", `
<li>
<div>${(+document.querySelector("#input").value).toFixed(2)}</div>
<button data-action="add">2</button>
</li>`);
}
function handle(evt) {
const origin = evt.target;
if (origin.id === "btn") {
return createElement();
}
if (origin.dataset.action) {
return addOrSubtract2(origin);
}
}
function addOrSubtract2(fromBttn) {
const parentLi = fromBttn.closest("li");
const add = fromBttn.dataset.action === "add";
const num = +(parentLi.querySelector("div").textContent) + (add ? 2 : -2);
fromBttn.dataset.action = add ? "subtract" : "add";
parentLi.querySelector("div").textContent = num.toFixed(2);
}
[data-action]:before {
content: attr(data-action)' ';
}
<input type="number" id="input" value=0 step="0.1">
<button id="btn">create new</button>
<ul id="ul"></ul>

Is this javasScript sorting function correct?

I'm a javaScript begginer. This function works and allows me to sorting list by ascendant and descendant by clicking in a button but I would like to know if it's properly written or maybe it can be simplified or written differently. Thanks in advance.
index.html
<p id="sort">Sort</p>
<ul>
<li>Phosphorus</li>
<li>Polonium</li>
<li>Radium</li>
<li>Fluorine</li>
<li>Iron</li>
<li>Hydrogen</li>
<li>Germanium</li>
<li>Mercury</li>
<li>Actinium</li>
<li>Barium</li>
<li>Calcium</li>
<li>Cadmium</li>
</ul>
<script src="main.js"></script>
main.js
const sortButton = document.querySelector("#sort");
let counter = 0;
eventListeners();
function eventListeners() {
sortButton.addEventListener("click", sortList);
}
function sortList() {
const elementsList = new Array();
const elements = document.querySelectorAll("ul li");
for (const element of elements) {
elementsList.push(element.textContent);
}
const ul = document.querySelector("ul");
ul.innerHTML = "";
if (counter % 2 === 0) {
const orderedList = elementsList.sort();
for (let i = 0; i < orderedList.length; i++) {
const li = document.createElement("li");
li.innerHTML = orderedList[i];
ul.appendChild(li);
}
counter++;
} else {
const orderedList = elementsList.reverse();
for (let i = 0; i < orderedList.length; i++) {
const li = document.createElement("li");
li.innerHTML = orderedList[i];
ul.appendChild(li);
}
counter++;
}
}
you don't have to clear the innerHTML of the ul, nor even ever use innerHTML
element.appendChild will move existing elements - as below
const sortButton = document.querySelector("#sort");
let counter = 0;
eventListeners();
function eventListeners() {
sortButton.addEventListener("click", sortList);
}
function sortList() {
const elementsList = Array.from(document.querySelectorAll("ul li"));
const ul = document.querySelector("ul");
elementsList.sort(({textContent:a}, {textContent:b})=>(counter%2?-1:1)*a.localeCompare(b))
.forEach(el => ul.appendChild(el));
counter++;
}
<p id="sort">Sort</p>
<ul>
<li>Phosphorus</li>
<li>Polonium</li>
<li>Radium</li>
<li>Fluorine</li>
<li>Iron</li>
<li>Hydrogen</li>
<li>Germanium</li>
<li>Mercury</li>
<li>Actinium</li>
<li>Barium</li>
<li>Calcium</li>
<li>Cadmium</li>
</ul>
In Es2015 (i.e. old school javascirpt) - this function looks like:
function sortList() {
var elementsList = Array.from(document.querySelectorAll("ul li"));
var ul = document.querySelector("ul");
elementsList.sort(function (_ref, _ref2) {
var a = _ref.textContent;
var b = _ref2.textContent;
return (counter % 2 ? -1 : 1) * a.localeCompare(b);
}).forEach(function (el) {
return ul.appendChild(el);
});
counter++;
}

create and add "li" element after click

In an exercise, I have created a new element li after each click of my button.
It doesn't work. In the console it reports "li is not defined".
What did I do wrong?
My code:
const btn = document.querySelector("button");
let number = 1;
const ul = document.querySelector("ul");
const fcn = function() {
const li = document.createElement('li');
li.textContent = number;
}
if (number % 3 == 0) {
li.classList.add('big');
}
ul.appendChild(li);
number += 2;
btn.addEventListener("click", fcn);
try this:
const btn = document.querySelector("button");
let number = 1;
const ul = document.querySelector("ul");
const fcn = function () {
const li = document.createElement('li');
li.textContent = number;
if (number % 3 == 0) {
li.classList.add('big');
}
ul.appendChild(li);
number += 2;
}
btn.addEventListener("click", fcn);
This problem is related to closures in javascript. li is not accessible outside function fcn.
following code should solve your problem.
const btn = document.querySelector("button");
let number = 0;
const ul = document.querySelector("ul");
const fcn = function () {
number += 1;
const li = document.createElement('li');
li.textContent = number;
ul.appendChild(li);
if (number % 3 == 0) {
li.classList.add('big');
}
}
btn.addEventListener("click", fcn);
li is defined within anonymous function.
When you define a variable/constant within function it's local to that function and can't be accessed outside it. It has function level scope.
To fix your code either define li outside of function at global scope or move the piece of code that use it within function itself as follows :
const btn =
document.querySelector("button");
let number = 1;
const ul =
document.querySelector("ul");
const fcn = function () {
const li =
document.createElement('li');
li.textContent = number;
if(number % 3 == 0){
li.classList.add('big');
}
ul.appendChild(li);
number += 2;
}
btn.addEventListener("click", fcn);

Add edit option for text inside <li> element using JavaScript

I want to change li.innerHtml when I click <li> it creates new input where I can change <li>'s text but after that I want to save it, it does not work well.
I need to change each <li> text when I click that <li>
function setText(elem, text){
elem.innerHTML += text;
}
var arr = ["one", "two", "three"];
for( var i = 0; i < arr.length; i++ ){
var li = document.createElement("li");
li.addEventListener("click", changeText);
setText(li, arr[i]);
var ul = document.createElement("ul");
document.body.appendChild(ul);
ul.appendChild(li);
}
var input = 0;
function changeText(){
input = document.createElement("input");
input.addEventListener("blur", saveText);
document.body.appendChild(input);
input.value = this.innerHTML;
}
function saveText(){
li.innerHTML = this.value;
}
You can assign an ID when creating the li and then add this id as a data attribute on input so you would know which input was changed and which li to update.
Here
function setText(elem, text){
elem.innerHTML += text;
}
var arr = ["one", "two", "three"];
for( var i = 0; i < arr.length; i++ ){
var li = document.createElement("li");
li.id = i;
li.addEventListener("click", changeText);
setText(li, arr[i]);
var ul = document.createElement("ul");
document.body.appendChild(ul);
ul.appendChild(li);
}
var input = 0;
function changeText(event){
input = document.createElement("input");
input.addEventListener("blur", saveText);
document.body.appendChild(input);
input.setAttribute("data-liID", event.target.id);
input.value = this.innerHTML;
}
function saveText(event){
document.getElementById(event.srcElement.getAttribute("data-liID")).innerHTML = this.value
}
Modify your function as below
function setText(elem, text){
elem.innerHTML += text;
}
var arr = ["one", "two", "three"];
var ul = document.createElement("ul");
document.body.appendChild(ul);
for( var i = 0; i < arr.length; i++ ){
var li = document.createElement("li");
li.addEventListener("click", changeText);
setText(li, arr[i]);
ul.appendChild(li);
}
var input = 0;
function changeText(){
input = document.createElement("input");
input.addEventListener("change", saveText);
document.body.appendChild(input);
input.value = this.innerHTML;
}
function saveText(){
var li=ul.children;
for (var liElement in li) {
li[liElement].innerHTML = this.value;
}
}
The main issue is, that li in saveText() is undefined. You have to reference it to the input somehow.
Also be aware that you are creating an ul container for each seperate li element. I do not know if that is on purpose. Yet it might be handy, if you see example two.
Example using data-attribute
Here is an example on how to do it with a data-attribute using your code with some slight changes (see comments):
//An array holding potential li-elements
var arr = ['one', 'two', 'three'];
//Creates an input element to change the text for the li element with
function changeText(){
var tIndex = this.getAttribute('data-index'); //The index we gave to the li-element on creating
var input = document.querySelector("input[data-index='" + tIndex + "']"); //Check if the input exists already
if(!input){
input = document.createElement('input'); //Make it local
input.setAttribute('data-index', this.getAttribute('data-index')); //We use this to find the correct li-element in saveText
input.addEventListener('blur', saveText);
document.body.appendChild(input);
input.value = this.innerHTML;
}
};
function setText(elem, text){
elem.innerHTML += text;
};
function saveText(){
var li = document.querySelector("li[data-index='" + this.getAttribute('data-index') + "']"); //Now with the index we can find our element
li.innerHTML = this.value;
};
window.onload = function(){
for(var i = 0; i < arr.length; i++){
var li = document.createElement('li');
li.setAttribute('data-index', i); //We use this to find the correct li-element in saveText
li.addEventListener('click', changeText);
setText(li, arr[i]);
var ul = document.createElement('ul');
document.body.appendChild(ul);
ul.appendChild(li);
}
}
Example using ul as parentNode for both
Another way is to add the input to the ul, so you can just access its parent:
//An array holding potential li-elements
var arr = ['one', 'two', 'three'];
//Creates an input element to change the text for the li element with
function changeText(){
var input = this.parentNode.querySelector('input'); //Check if exists yet
if(!input){
input = document.createElement('input');
input.addEventListener('blur', saveText);
this.parentNode.appendChild(input); //We add the input to the ul instead of the body
input.value = this.innerHTML;
}
};
function setText(elem, text){
elem.innerHTML += text;
};
function saveText(){
var li = this.parentNode.firstChild; //firstchild is the li in this case
li.innerHTML = this.value;
};
window.onload = function(){
for(var i = 0; i < arr.length; i++){
var li = document.createElement('li');
li.addEventListener('click', changeText);
setText(li, arr[i]);
var ul = document.createElement('ul');
document.body.appendChild(ul);
ul.appendChild(li);
}
}
Example using contentEditable
It can also be done by making the li elements editable:
https://developer.mozilla.org/de/docs/Web/API/HTMLElement/contentEditable
var arr = ['one', 'two', 'three'];
window.onload = function(){
var ul = document.createElement('ul');
for(var i = 0; i < arr.length; i++){
var li = document.createElement('li');
li.textContent = arr[i];
li.contentEditable = true;
ul.appendChild(li);
};
document.body.appendChild(ul);
}

Categories

Resources