Toggle like button for multiple elements with js - javascript

I'm a JS beginner and I'm trying to create a personal project, but I have some problems with implementing a toggle like button for multiples pictures.
below u can see my button but is working only to one photo. how can a create a loop for the same button to work on every picture that a have in my project? Please help me!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="https://pro.fontawesome.com/releases/v5.10.0/css/all.css" integrity="sha384-AYmEC3Yw5cVb3ZcuHtOA93w35dYTsvhLPVnYs9eStHfGJvOvKxVfELGroGkvsg+p" crossorigin="anonymous"/>
<script src="index.js" defer></script>
<link rel="stylesheet" href="index.css">
</head>
<body>
<button class="like__btn">
<span id="icon"><i class="far fa-thumbs-up"></i></span>
<span id="count">0</span> Like
</button>
</body>
</html>
JS:
const likeBtn = document.querySelector(".like__btn");
let likeIcon = document.querySelector("#icon");
count = document.querySelector("#count");
let clicked = false;
likeBtn.addEventListener("click", () => {
if (!clicked) {
clicked = true;
likeIcon.innerHTML = `<i class="fas fa-thumbs-up"></i>`;
count.textContent++;
} else {
clicked = false;
likeIcon.innerHTML = `<i class="far fa-thumbs-up"></i>`;
count.textContent--;
}
});

You need to use class instead of id attribute for span tags and then
get all buttons by querySelectorAll then create a loop and attach click event to those buttons.
Here is working sample:
const likeBtn = document.querySelectorAll(".like__btn");
likeBtn.forEach(function (item) {
item.addEventListener("click", (event) => {
let likeIcon = item.querySelector(".icon");
let count = item.querySelector(".count");
if (+count.textContent == 0) {
clicked = true;
likeIcon.innerHTML = `<i class="fas fa-thumbs-up"></i>`;
count.textContent++;
} else {
clicked = false;
likeIcon.innerHTML = `<i class="far fa-thumbs-up"></i>`;
count.textContent--;
}
});
})
<link rel="stylesheet" href="https://pro.fontawesome.com/releases/v5.10.0/css/all.css" />
<button class="like__btn">
<span class="icon"><i class="far fa-thumbs-up"></i></span>
<span class="count">0</span> Like
</button>
<button class="like__btn">
<span class="icon"><i class="far fa-thumbs-up"></i></span>
<span class="count">0</span> Like
</button>
<button class="like__btn">
<span class="icon"><i class="far fa-thumbs-up"></i></span>
<span class="count">0</span> Like
</button>

Related

Javascript timer with a whileloop crashes the page

I'm trying to make a simple countup timer with a start/stop button. I don't understand why the while loop crashes the page even though it has a 1sec delay. Is there a easier way to keep updating the time until I press the button?
let startButton = document.getElementById("btn-start-stop");
let timerOutput = document.getElementById("timer");
let runTimer = false;
let sec = 0;
let startTimer = false;
console.log(startTimer);
startButton.onclick = function () {
startTimer = !startTimer;
while (startTimer) {
setInterval(function () {
console.log(sec);
}, 1000);
}
};
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="an exercise to manipulate DOM-elements">
<meta name="author" content="lars berg">
<meta name="keywords" content="a template for at exercise">
<link rel="stylesheet" type="text/css" href="css/style.css">
<title>DOM | Manipulate</title>
<script src="https://kit.fontawesome.com/fa41e523cd.js" crossorigin="anonymous"></script>
<script src="scripts/script.js" defer></script>
</head>
<body>
<header>
<h1>DOM | Manipulation</h1>
</header>
<main>
<div class="flex-container">
<div class="counter-container">
<h2 class="h2-counter">How long time will it take to read this information?</h2>
<div id="timer">
00:00:00
</div>
<div class="buttons">
<button id="btn-start-stop" type="button">
<i class="fa-solid fa-play fa-2x"></i>
</button>
<button id="btn-reset" type="button">
<i class="fa-solid fa-arrow-rotate-left fa-2x"></i>
</button>
</div>
</div>
</div>
</main>
<footer>
</footer>
</body>
</html>
You should not use a while() loop as this will block the thread.
Use setInterval() to preform an action at a specific interval (1000ms), and use clearInterval() to stop it on second press
Regarding the seconds to HMS, I'ved used this SO one-liner and made a function of it that will be called after we've bumped the seconds.
let startButton = document.getElementById("btn-start-stop");
let timerOutput = document.getElementById("timer");
let sec = 0;
let timer = null;
startButton.onclick = function () {
if (timer) {
clearInterval(timer);
timer = null;
} else {
timer = setInterval(() => ++sec && update(), 1000);
}
};
function update() {
timerOutput.innerHTML = new Date(sec * 1000).toISOString().substr(11, 8);
}
<script src="https://kit.fontawesome.com/fa41e523cd.js" crossorigin="anonymous"></script>
<header>
<h1>DOM | Manipulation</h1>
</header>
<main>
<div class="flex-container">
<div class="counter-container">
<h2 class="h2-counter">How long time will it take to read this information?</h2>
<div id="timer">
00:00:00
</div>
<div class="buttons">
<button id="btn-start-stop" type="button">
<i class="fa-solid fa-play fa-2x"></i>
</button>
<button id="btn-reset" type="button">
<i class="fa-solid fa-arrow-rotate-left fa-2x"></i>
</button>
</div>
</div>
</div>
</main>
While loop will block your thread because JavaScript is single threaded language.
let startButton = document.getElementById("btn-start-stop");
let timerOutput = document.getElementById("timer");
let runTimer = false;
let sec = 0;
let startTimer = false;
console.log(startTimer);
startButton.onclick = function () {
// you can change your button here
setInterval(function () {
console.log(sec);
sec = sec+1
// implement your logic here for seconds
}, 1000);
};
Because while looping, you are generating a call stack of setTimeout again and again which seems to crash the browser stack
check below sample code while no loop just use recursive approach
let startButton = document.getElementById("btn-start-stop");
let timerOutput = document.getElementById("timer");
let runTimer = false;
let sec = 0;
let startTimer = false;
let clearTime
let count = 0
startButton.onclick = function () {
clearTime = setTimeout("startWatch( )", 1000)
};
function startWatch(){
setInterval(function () {
console.log(count);
count++
}, 1000);
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="an exercise to manipulate DOM-elements">
<meta name="author" content="lars berg">
<meta name="keywords" content="a template for at exercise">
<link rel="stylesheet" type="text/css" href="css/style.css">
<title>DOM | Manipulate</title>
<script src="https://kit.fontawesome.com/fa41e523cd.js" crossorigin="anonymous"></script>
<script src="scripts/script.js" defer></script>
</head>
<body>
<header>
<h1>DOM | Manipulation</h1>
</header>
<main>
<div class="flex-container">
<div class="counter-container">
<h2 class="h2-counter">How long time will it take to read this information?</h2>
<div id="timer">
00:00:00
</div>
<div class="buttons">
<button id="btn-start-stop" type="button">
<i class="fa-solid fa-play fa-2x"></i>
</button>
<button id="btn-reset" type="button">
<i class="fa-solid fa-arrow-rotate-left fa-2x"></i>
</button>
</div>
</div>
</div>
</main>
<footer>
</footer>
</body>
</html>

Fontawesome can't change the class because of invalid string characters

const toggleDarkOrLight = document.getElementsByTagName('i')[0];
var toggled = false;
const toggle = () => {
if (toggled === false) {
toggleDarkOrLight.classList.remove('fa fa-toggle-off');
toggleDarkOrLight.classList.add('fa fa-toggle-on');
} else {
toggleDarkOrLight.classList.remove('fa fa-toggle-on');
toggleDarkOrLight.classList.add('fa fa-toggle-off');
}
}
<html>
<head>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
</head>
<body>
<i class="fa fa-toggle-off" onclick="toggle()"></i>
</body>
<html>
Why can't I change the class? Is it because of the "-" character
What should I try instead then?
Also, how can I make the toggle on and off like a smooth animation (or I can't?)
It's the space( ) thats interfering!
Since your adding, and removing the fa class, just let it be.
That said, consider using classList.contains to check witch class you want to toggle.
const toggleDarkOrLight = document.getElementsByTagName('i')[0];
const toggle = () => {
if (toggleDarkOrLight.classList.contains('fa-toggle-off')) {
toggleDarkOrLight.classList.remove('fa-toggle-off');
toggleDarkOrLight.classList.add('fa-toggle-on');
} else {
toggleDarkOrLight.classList.remove('fa-toggle-on');
toggleDarkOrLight.classList.add('fa-toggle-off');
}
}
<html>
<head>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
</head>
<body>
<i class="fa fa-toggle-off" onclick="toggle()"></i>
</body>
<html>
No, it's the space. classList.add/.remove expect a single class name and a class name may not contain a space. In order to add/remove multiple classes, but them in separate arguments:
toggleDarkOrLight.classList.remove('fa', 'fa-toggle-off');
toggleDarkOrLight.classList.add('fa', 'fa-toggle-on');
(Or just leave faout, since you are both adding and removing it.)
You don't actually want to toggle the fa part at all. You only want to toggle the fa-toggle-off/on part. Here is how your code should look:
const toggleDarkOrLight = document.getElementsByTagName("i")[0];
var toggled = false;
const toggle = () => {
if (toggled === false) {
toggleDarkOrLight.classList.remove("fa-toggle-off");
toggleDarkOrLight.classList.add("fa-toggle-on");
toggled = true;
} else {
toggleDarkOrLight.classList.remove("fa-toggle-on");
toggleDarkOrLight.classList.add("fa-toggle-off");
toggled = false;
}
};
<html>
<head>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
</head>
<body>
<i class="fa fa-toggle-off" onclick="toggle()"></i>
</body>
<html>

How to add popover to dynamically created list items

I have the following code. Clicking on the existing list item shows the popover.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Test</title>
<script src="https://cdn.jsdelivr.net/npm/jquery#3.5.1/dist/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap#4.5.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
$(function () {
$('[data-toggle="popover"]').popover()
})
</script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap#4.5.0/dist/css/bootstrap.min.css"/>
</head>
<body>
<div class="container">
<div class="card" style="width: 18rem;">
<div class="card-header">
Featured
</div>
<ul id="card-list" class="list-group list-group-flush">
<li id="card-item" class="list-group-item" data-toggle="popover" title="Rule Details" data-placement="top" data-html="true" data-content="<h3>Popover Content</h3>">Cras justo odio</li>
</ul>
</div></br>
<button id="test" type="button" class="btn btn-outline-dark btn-sm">Test</button>
</div>
</body>
</html>
The above code has a Test button to add items to the current list. I use the code below to define new items during which I set popover attributes. New list items can be added but clicking on them doesn't bring up popovers. Please help!
let listItem = document.createElement('li');
listItem.innerHTML = "Test";
listItem.className = "list-group-item";
listItem.setAttribute("data-toggle", "popover");
listItem.setAttribute("title", "Rule Details");
listItem.setAttribute("data-placement", "top");
listItem.setAttribute("data-html", "true");
listItem.setAttribute("data-content", "<strong>test Hello World</strong>");
document.getElementById("card-list").appendChild(listItem);
You have to initialize popover on the newly created elements. Since popover is a jQuery plugin convert your element to jQuery object and apply popoper before appending like:
let listItem = document.createElement('li');
listItem.innerHTML = "Test";
listItem.className = "list-group-item";
listItem.setAttribute("data-toggle", "popover");
listItem.setAttribute("title", "Rule Details");
listItem.setAttribute("data-placement", "top");
listItem.setAttribute("data-html", "true");
listItem.setAttribute("data-content", "<strong>test Hello World</strong>");
listItem = $(listItem);
listItem.popover();
$("#card-list").append(listItem);

LocalStorage removes the wrong value

I keep having the same issue again and again : When I add a task, it gets added to the localStorage correctly. But once I have multiple tasks and I click complete on a random one, sometimes it happens that the LocalStorage removes the task before it or after it. I can't figure out a way how to let it work.
let addBtn = document.getElementById("addButton");
let textInput = document.getElementById("textInput");
let mainList = document.getElementById("mainList");
let storage = JSON.parse(localStorage.getItem("toDos")) || [];
if (storage.length != 0){
for (let i=0; i<storage.length;i++){
addTask(mainList, storage[i]);
};
};
function addTask(list,task){
var newTask = document.createElement("li");
newTask.textContent=task
newTask.className="task d-flex justify-content-between list-group-item bg-primary mt-2";
addCompleteBtn(newTask);
list.appendChild(newTask);
}
function addCompleteBtn (newTask){
let completeBtn = document.createElement("button");
completeBtn.className="btn btn-success completeBtn";
completeBtn.textContent="Completed";
newTask.appendChild(completeBtn);
};
addBtn.addEventListener("click", ()=>{
if(textInput.value === ""){
alert("Error: The field is either empty or you entered more than 100 characters!");
}else{
storage.push(textInput.value);
addTask(mainList, textInput.value);
textInput.value="";
localStorage.setItem("toDos",JSON.stringify(storage));
};
});
mainList.addEventListener("click",(evt)=>{
if (evt.target.className==="btn btn-success completeBtn") {
let listItem = evt.target.parentNode;
let mainUl = listItem.parentNode;
let toBeRemoved = listItem.textContent;
storage.splice(storage.indexOf(toBeRemoved),1);
mainUl.removeChild(listItem);
localStorage.setItem("toDos",JSON.stringify(storage));
};
});
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<link rel="stylesheet" href="style.css">
<title>To-do-list App</title>
</head>
<body>
<div class="jumbotron bg-info">
<div class="container">
<h1 class="display-4 text-center">Task List</h1>
<p class="lead text-center">Click on the Add button to add a task</p>
</div>
</div>
<div class="container">
<div class="firstInput input-group mb-5">
<div class="input-group-prepend">
<button class="btn btn-primary" type="button" id="addButton">Add Task</button>
</div>
<input type="text" title='Enter a Task' class="form-control" id="textInput" maxlength="200">
</div>
</div>
<ul class='tasksList container' id="mainList">
</ul>
<script src="script.js"></script>
</body>
</html>
Shows all the values in the LocalStorage
Once task 2 is removed, task 3 is removed from the Storage instead
this is the specific part that causes me issues :
mainList.addEventListener("click",(evt)=>{
if (evt.target.className==="btn btn-success completeBtn") {
let listItem = evt.target.parentNode;
let mainUl = listItem.parentNode;
let toBeRemoved = listItem.textContent;
/* This is the part of code causing me issues
storage.splice(storage.indexOf(toBeRemoved),1);
*/
mainUl.removeChild(listItem);
localStorage.setItem("toDos",JSON.stringify(storage));
};
});
The problem is here let toBeRemoved = listItem.textContent;,this will return the text content of all elements inside that li. So if you add a task named 'Task 1', when you press Completed the value of toBeRemoved will be Task 1Completed since this will return the text of the button as well since your button is a child of the li.
So change let toBeRemoved = listItem.textContent; to let toBeRemoved = listItem.childNodes[0].nodeValue; and you should be fine, this will get the text of the li only.

How to delete <li> element clicking on <i> element in java script? [duplicate]

This question already has answers here:
What do querySelectorAll and getElementsBy* methods return?
(12 answers)
Closed 4 years ago.
I am still learning JS and have some small problem with deleting element clicking on element in java script. Maybe somebody can help or give advice?
Here is my code:
var add = document.getElementById("add-button");
add.addEventListener("click", function() {
var ul = document.getElementById("tasks");
var input = document.getElementById("new-task").value;
var li = document.createElement("li");
ul.appendChild(li);
li.innerText = input;
var i = document.createElement("i");
i.classList.add('fas', 'fa-times');
// i.className = 'fas fa-times';
li.appendChild(i);
input.value = "";
});
var remove = document.querySelectorAll(".fas");
remove.addEventListener("click", function() {
for (var i = 0; i < remove.length; i++) {
remove[i].classList.remove('remove-list');
}
});
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.3.1/css/all.css" integrity="sha384-mzrmE5qonljUremFsqc01SB46JvROS7bZs3IO2EmfFsd15uHvIt+Y8vEf7N7fWAU" crossorigin="anonymous">
</head>
<body>
<div class="wrapper">
<div class="to-do-container">
<div class="input-data">
<input id="new-task" type="text">
<button id="add-button" type="button">Add</button>
</div>
<ul id="tasks">
<li class="remove-list">Zadzwonić do...<i id="fas" class="fas fa-times"></i></li>
<li class="remove-list">Odebrać dzieci z...<i class="fas fa-times"></i></li>
<li class="remove-list">Kupić na obiad...<i class="fas fa-times"></i></li>
<li class="remove-list">Umówić się na...<i class="fas fa-times"></i></li>
</ul>
</div>
</div>
</body>
</html>
I have used loop as I think when I am using the querySelectorAll it is necessary to go through the loop. Thank you in advance for help.
you can write a function to remove parent li and call this function inside addEventLister which you need to attach while creating i
var add = document.getElementById("add-button");
add.addEventListener("click", function() {
var ul = document.getElementById("tasks");
var input = document.getElementById("new-task").value;
var li = document.createElement("li");
ul.appendChild(li);
li.innerText = input;
var i = document.createElement("i");
i.classList.add('fas', 'fa-times');
i.addEventListener("click", removeLi);
// i.className = 'fas fa-times';
li.appendChild(i);
input.value = "";
});
var remove = document.getElementsByClassName("fas fa-times");
for (var i = 0; i < remove.length; i++) {
remove[i].addEventListener('click',removeLi);
}
function removeLi() {
this.parentElement.remove();
}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.3.1/css/all.css" integrity="sha384-mzrmE5qonljUremFsqc01SB46JvROS7bZs3IO2EmfFsd15uHvIt+Y8vEf7N7fWAU" crossorigin="anonymous">
</head>
<body>
<div class="wrapper">
<div class="to-do-container">
<div class="input-data">
<input id="new-task" type="text">
<button id="add-button" type="button">Add</button>
</div>
<ul id="tasks">
<li class="remove-list">Zadzwonić do...<i id="fas" class="fas fa-times"></i></li>
<li class="remove-list">Odebrać dzieci z...<i class="fas fa-times"></i></li>
<li class="remove-list">Kupić na obiad...<i class="fas fa-times"></i></li>
<li class="remove-list">Umówić się na...<i class="fas fa-times"></i></li>
</ul>
</div>
</div>
</body>
</html>

Categories

Resources