I would like to implement such logic:
User clicks on "Plus" or "Minus" button.
If the user don't click at any of those buttons for 2 seconds, then we assume that current quantity should be sent to the server.
Right now, I have three buttons:
"Plus" - increments quantity by 1 and changes the value at page.
"Minus" - decreases quantity by 1 and changes the value at page.
"Confirm" - The button that sends request with current quantity parameter to Spring Boot controller and changes quantity on server side.
I would prefer to avoid this button, because it adds complexity.
Is there a convenient way to get rid of the confirm button?
The only way I know how we can do this is by sending request to controller on each "Plus" or "Minus" button click.
But it seems that this approach will be inefficient.
$(document).ready(function () {
//include csrf for every ajax call
$(function () {
let token = $("meta[name='_csrf']").attr("content");
let header = $("meta[name='_csrf_header']").attr("content");
$(document).ajaxSend(function (event, xhr, options) {
xhr.setRequestHeader(header, token);
});
});
$(".plusForm").submit(function (event) {
event.preventDefault();
let $prodCount = $(this).parent().parent().parent().find(".prodCount span");
let currentQuantity = parseInt($prodCount.text());
$prodCount.text(++currentQuantity);
});
$(".minusForm").submit(function (event) {
event.preventDefault();
let $prodCount = $(this).parent().parent().parent().find(".prodCount span");
let currentQuantity = parseInt($prodCount.text());
$prodCount.text(--currentQuantity);
});
$(".changedQuantityForm").submit(function (event) {
event.preventDefault();
let $prodCount = $(this).parent().parent().parent().find(".prodCount span");
let quantity = parseInt($prodCount.text());
let productId = $(this).parent().parent().parent().parent().find(
'.product-id').val();
changeQuantityAjax(productId, quantity);
});
function changeQuantityAjax(id, quantity) {
console.log("quantity changed on server side");
/* $.ajax({
type: "PUT",
contentType: "application/json",
url: "/rest/cart/" + id + "?quantity=" + quantity,
data: {
"quantity": quantity
},
success: function () {
console.log('SUCCESS ' + id + ' ' + quantity);
// alert(name + ' was deleted')
},
error: function () {
console.log('ERROR ' + id + ' ' + quantity);
}
}); */
}
})
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css"
rel="stylesheet" media="screen"/>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.5.0/css/all.css">
<div class="row justify-content-center">
<div class="col mb-4 prodItem">
<div class="card border-primary" style="height: 34rem; width: 26rem;">
<div class="view overlay">
<img class="card-img-top img-fluid"
src="https://cdn.pixabay.com/photo/2018/10/05/23/24/chicken-3727097_1280.jpg"
style="height : 18rem;" alt="Card image cap">
<div class="mask rgba-white-slight"></div>
</div>
<div class="card-body">
<h3><span>Chicken</span></h3>
<div class="float-right">
<h2 class="card-title"><span>1000</span> $</h2>
</div>
<br>
<br>
<br>
<div class="form-control">
<div class="row prodCount" style="margin: auto; font-size: 17px">
<p> In cart :  </p>
<span>0</span>
<div class="row float-right" style="margin: auto">
<form class="plusForm">
<button type="submit" class="btn-sm">
<i class="fas fa-plus-circle fa-w-16 fa-3x text-danger"></i>
</button>
</form>
<form class="minusForm">
<button type="submit" class="btn-sm">
<i class="fa fa-minus-circle fa-w-16 fa-3x" aria-hidden="true"></i>
</button>
</form>
<form class="changedQuantityForm">
<button type="submit" class="btn-sm">
<i class="fas fa-check fa-w-16 fa-3x text-success"></i>
</button>
</form>
</div>
</div>
</div>
<br>
</div> <!-- card-body -->
</div> <!-- card -->
</div>
Fiddle
PC: It seems that my question is related to this topic(https://stackoverflow.com/a/7762539/14308420).
But I'm not sure if I should apply it in my case and how to do it.
I extracted the logic of submit button to function updateQuantityOnServer(button). And after adding this line:
changedQuantityTimeout = setTimeout(updateQuantityOnServer(this), 1000);
I got a warning:
Argument type void is not assignable to parameter type TimerHandler Type void is not assignable to type string | Function Type void is not assignable to type Function.
As I understand, it's caused because I send button as parameter. But I use this button to get parameters...
Look at the changeQuantityAjax function for the implementation of clearTimeout and setTimeout together, making a "minimal" delay of 2 seconds after the last user action.
From each button click, this was passed to the getIdandQuantity function.
It does not change much things in the logic, but notice the .parents() instead of parent().parent().parent().
$(document).ready(function() {
//include csrf for every ajax call
$(function() {
let token = $("meta[name='_csrf']").attr("content");
let header = $("meta[name='_csrf_header']").attr("content");
$(document).ajaxSend(function(event, xhr, options) {
xhr.setRequestHeader(header, token);
});
});
$(".plusForm").submit(function(event) {
event.preventDefault();
let $prodCount = $(this).parents(".prodCount").find("span");
let currentQuantity = parseInt($prodCount.text());
$prodCount.text(++currentQuantity);
getIdandQuantity(this);
});
$(".minusForm").submit(function(event) {
event.preventDefault();
let $prodCount = $(this).parents(".prodCount").find("span");
let currentQuantity = parseInt($prodCount.text());
$prodCount.text(--currentQuantity);
getIdandQuantity(this);
});
// That is the function to retreive the id and quantity from the clicked button
function getIdandQuantity(btn) {
let $parent = $(btn).parents(".prodCount");
let quantity = parseInt($parent.find("span").text());
let productId = $parent.find('.product-id').val();
changeQuantityAjax(productId, quantity);
}
// A variable to store the refence to the pending setTimeout
let ajaxTimeout
function changeQuantityAjax(id, quantity) {
// Clear any existing setTimeout
clearTimeout(ajaxTimeout)
// Set a 2 seconds timeout
ajaxTimeout = setTimeout(function() {
console.log("quantity changed on server side");
/* $.ajax({...}) */
}, 2000)
}
})
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="stylesheet" media="screen" />
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.5.0/css/all.css">
<div class="row justify-content-center">
<div class="col mb-4 prodItem">
<div class="card border-primary" style="height: 34rem; width: 26rem;">
<div class="view overlay">
<img class="card-img-top img-fluid" src="https://cdn.pixabay.com/photo/2018/10/05/23/24/chicken-3727097_1280.jpg" style="height : 18rem;" alt="Card image cap">
<div class="mask rgba-white-slight"></div>
</div>
<div class="card-body">
<h3><span>Chicken</span></h3>
<div class="float-right">
<h2 class="card-title"><span>1000</span> $</h2>
</div>
<br>
<br>
<br>
<div class="form-control">
<div class="row prodCount" style="margin: auto; font-size: 17px">
<p> In cart :  </p>
<span>0</span>
<div class="row float-right" style="margin: auto">
<form class="plusForm">
<button type="submit" class="btn-sm">
<i class="fas fa-plus-circle fa-w-16 fa-3x text-danger"></i>
</button>
</form>
<form class="minusForm">
<button type="submit" class="btn-sm">
<i class="fa fa-minus-circle fa-w-16 fa-3x" aria-hidden="true"></i>
</button>
</form>
</div>
</div>
</div>
<br>
</div>
<!-- card-body -->
</div>
<!-- card -->
</div>
Additionnally! You could easilly merge the 4 functions above (2 click handlers, getIdandQuantity and changeQuantityAjax) into one single click handler.
$(document).ready(function() {
//include csrf for every ajax call
$(function() {
let token = $("meta[name='_csrf']").attr("content");
let header = $("meta[name='_csrf_header']").attr("content");
$(document).ajaxSend(function(event, xhr, options) {
xhr.setRequestHeader(header, token);
});
});
// A variable to store the refence to the pending setTimeout
let ajaxTimeout
$(".plusForm, .minusForm").submit(function(event) {
event.preventDefault();
// Get the elements needed, the product is and the quantity shown
let $parent = $(this).parents(".prodCount");
let id = $parent.find('.product-id').val();
let $prodCount = $parent.find("span")
let currentQuantity = parseInt($prodCount.text());
// Increment OR decrement the quantity
let quantity = ($(this).hasClass("plusForm")) ? ++currentQuantity : --currentQuantity
// Update the shown quantity
$prodCount.text(quantity)
// Clear any existing setTimeout
clearTimeout(ajaxTimeout)
// Set a 2 seconds timeout
ajaxTimeout = setTimeout(function() {
console.log("quantity changed on server side");
/* $.ajax({...}) */
}, 2000)
});
})
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="stylesheet" media="screen" />
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.5.0/css/all.css">
<div class="row justify-content-center">
<div class="col mb-4 prodItem">
<div class="card border-primary" style="height: 34rem; width: 26rem;">
<div class="view overlay">
<img class="card-img-top img-fluid" src="https://cdn.pixabay.com/photo/2018/10/05/23/24/chicken-3727097_1280.jpg" style="height : 18rem;" alt="Card image cap">
<div class="mask rgba-white-slight"></div>
</div>
<div class="card-body">
<h3><span>Chicken</span></h3>
<div class="float-right">
<h2 class="card-title"><span>1000</span> $</h2>
</div>
<br>
<br>
<br>
<div class="form-control">
<div class="row prodCount" style="margin: auto; font-size: 17px">
<p> In cart :  </p>
<span>0</span>
<div class="row float-right" style="margin: auto">
<form class="plusForm">
<button type="submit" class="btn-sm">
<i class="fas fa-plus-circle fa-w-16 fa-3x text-danger"></i>
</button>
</form>
<form class="minusForm">
<button type="submit" class="btn-sm">
<i class="fa fa-minus-circle fa-w-16 fa-3x" aria-hidden="true"></i>
</button>
</form>
</div>
</div>
</div>
<br>
</div>
<!-- card-body -->
</div>
<!-- card -->
</div>
Related
I'm having issue on getting the data from JSON to display it on a html element.
I try many ways but none is working
This is my code:
MYAPP.JS
$(document).ready(function() {
$(function() {
switch (menu) {
case 'sign in':
$('#signin').addClass('active');
break;
case 'vendors':
$('#vendors').addClass('active');
break;
case 'view passports':
$('#listpassports').addClass('active');
break;
case 'contact':
$('#contact').addClass('active');
break;
case 'home':
$('#home').addClass('active');
break;
}
});
AOS.init({
'duration': 1250
}
);
var jsonUrl = '';
if (window.categoryID == '') {
jsonUrl = window.contextRoot + '/json/data/all/passbeers';
}
else {
jsonUrl = window.contextRoot + '/json/data/category/' + window.categoryID + '/passbeers';
}
});
JSONCONTROLLER
package com.itconnect.lisboabeerpassport.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.itconnect.lisboabeerpassportbackend.dao.PassbeerDAO;
import com.itconnect.lisboabeerpassportbackend.dto.Passbeer;
#Controller
#RequestMapping("json/data")
public class JsonDataController
{
#Autowired
private PassbeerDAO passbeerDAO;
#RequestMapping("/all/passbeers")
#ResponseBody
public List < Passbeer > getAllPassbeer() {
return passbeerDAO.listActivePassbeer();
}
#RequestMapping("/category/{id}/passbeers")
#ResponseBody
public List < Passbeer > getPassbeerByCategory(#PathVariable int id) {
return passbeerDAO.listActivePassbeersByCategory(id);
}
}
HTML PAGE TO DISPLAY THE JSON DATA
<div class="container">
<%#include file="./shared/navbar.jsp"%>
<c:if test="${userclickAllpassbeers == true}">
<script>
window.categoryID = '';
</script>
</c:if>
<c:if test="${userclickSingleCategoryPassbeer == true}">
<script>
window.categoryID = '${category.id}';
</script>
</c:if>
<div class="row">
<div class="col-sm-12 text-center font-weight-normal">
<h2 class="cat_">CATEGORIES</h2>
</div>
</div>
<div class="row">
<c:forEach items="${categories}" var="category">
<div class="col-lg-2 col-sm-4 mb-4 mx-auto">
<!-- Portfolio item 2-->
<div class="portfolio-item">
<img class="img-fluid" src="${contextRoot}/resources/img/${category.imageURL}" "
alt="... " />
</a>
<div class="portfolio-caption ">
<div class="portfolio-caption-heading ">
<h5 class="card-title text-center ">
${category.name} PASS
</h5>
</div>
</div>
</div>
</div>
</c:forEach>
</div>
<div class="row ">
<div class="product-box col-md-4 ">
<div class="product-inner-box position-relative ">
<div class="icons position-absolute top-0 start-0 ">
<i class="fa-solid fa-eye "></i>
</div>
<!-- end /icons -->
<!-- end /icons -->
<img src="${contextRoot}/resources/img/portugal-passport.png "/>
<div class="cart-btn ">
<button class="btn btn-success shadow-sm "><i class="fa-solid fa-cart-shopping "></i>Add to cart</button>
</div>
<!-- end /cart-btn -->
</div>
<!-- end /product-inner-box -->
<div class="product-info ">
<div class="product-name ">
<spam>Premium Passport</spam>
</div>
<!-- end product name -->
<div class="product-price ">
$<spam>13</spam>
</div>
<!-- end product price -->
</div>
<!-- end product-info -->
</div>
<!-- end product-box -->
</div>
<!-- end div show passports -->
<div class="row ">
<div class="col-lg-12 ">
<div id="passbeerimage "></div>
</div>
</div>
</div>
I need help for this can't find a way to do it.
I try to use
$.ajax({ dataType: "json", url: url, data: data, success: success});
but I want the working way of getting JSON data AND display it on html tag
I have a solution that how to get JSON data then update to the HTML elements by using a Vanilla JS then I made my own examples as your reference follow these steps below
1. Create a HTML file named index.html
<!DOCTYPE html>
<html>
<body>
<div id="root">
<p id="name">
Name: {name}
</p>
<p id="birthdate">
Birthdate: {birthdate}
</p>
<p id="age">
Age: {age}
</p>
<p id="address">
Location: {location}
</p>
</div>
<script src="parser.js" type="text/javascript"></script>
</body>
</html>
2. Second create a JavaScript file named parser.js
// Initialize the headers
let init = {
method: "GET",
headers: {
"Content-Type": "application/json"
},
mode: "cors",
cache: "default"
};
let parseJSON = new Request("data.json", init);
// Use Fetch() API to get JSON data and update to the HTML element.
fetch(parseJSON).then((response) => {
return response.json();
}).then((data) => {
let name = document.querySelector("#name");
let birthdate = document.querySelector("#birthdate");
let age = document.querySelector("#age");
let adddress = document.querySelector("#location");
name.textContent = `Name: ${data.name}`;
birthdate.textContent = `Birthdate: ${data.birthdate}`;
age.textContent = `Age: ${data.age}`;
address.textContent = `Location: ${data.address}`;
});
3. Finally create JSON file named data.json
{
"name": "Yer",
"birthdate": "January 1, 2003",
"age": "19",
"address": "Philippines"
}
const renderNote = data => {
const postListRef = ref(db, 'Notes/' +data.key);
console.log(data.key)
const newPostRef = push(postListRef);
var status = 'Pending'
var title = 'new note'
var date = '29-4-2022'
var note = 'newly added note'
let card =
`<div id="single-card" class="col-lg-4 col-md-3" data-id=${data.key} ><!--outer layer of single card-->
<div class="card card-body"><!--card body-->
<p class="badge" id="status" style="background-color: rgb(0, 81, 81);">${status}</p>
<span class="side-stick"></span> <!--side-stick color-->
<!-- note title -->
<h5 class="note-title text-truncate w-75 mb-0" >${title}<i class="point fa fa-circle ml-1 font-10"></i></h5><!--fa fa-circle is for the dot dot dot(continuity)-->
<p class="note-date font-12 text-muted mt-0">${date}</p>
<!--note description-->
<div class="note-content">
<p class="note-inner-content text-muted" >${note}<i class="point fa fa-circle ml-1 font-10"></i></p>
</div>
<button class="btn btn-del">Delete${data.key}</button>
<div id="actions" >
</div>
</div>
</div>`
prod.innerHTML += card;
const btnDelete = document.querySelector(`[data-id='${data.key}'] .btn-del`);
console.log(btnDelete);
btnDelete.addEventListener("click",()=>{
console.log('deleting');
});
}
EventListener is not working. But when I print the btnDelete(console.log(btnDelete);) it is printing correctly. But the eventlistener is not workingDoes anybody know what is wrong with the code?
Why donĀ“t you use onclick in HTML?
<button onclick="console.log('deleting');" class="btn btn-del">Delete${data.key}</button>
You can even call a delete Function like that:
HTML:
<button onclick="deleteCard(${data.key})" class="btn btn-del">Delete${data.key}</button>
JavaScript:
function deleteCard(key) {
console.log(`Delete Card with key: ${key}`)
}
Whenever I am writing apple or samsung, It is showing results, but if I don't refresh the page for the second time, it gives me error. However, If I reload the website, then it is showing me the results of samsung or oppo or whatever. Can you please tell me where I made the mistake ? I am attaching the HTML and JS below :
// error handle
const displayHandle = (direction, id) => {
const errorMessage = document.getElementById(id);
errorMessage.style.display = direction;
}
// clear previous result
const clearSearchResult = (direction) => {
const searchResult = document.getElementById(direction);
searchResult.innerHTML = '';
}
// details button action form
const allDetails = (id) => {
// console.log(id)
fetch(`https://openapi.programming-hero.com/api/phone/${id}`)
.then(response => response.json())
.then(data => {
const sensorName = data.data.mainFeatures.sensors;
let storeSensorName = '';
for (const sensor of sensorName) {
storeSensorName = `${storeSensorName} ${sensor}, `
}
const mobileDetails = document.getElementById('show-details');
mobileDetails.innerHTML = `
<div class=" row row-cols-1 row-cols-md-3 g-4">
<div class="col">
<div class="card">
<img src=${data.data.image} class="card-img-top w-50 h-50" alt="...">
<div id="details-body" class="card-body">
<h5 class="card-title">Brand: ${data.data.brand}</h5>
<p class="card-text">Storage: ${data.data.mainFeatures.storage}</p>
<p class="card-text">ChipSet: ${data.data.mainFeatures.chipSet}</p>
<p class="card-text">DisplaySize: ${data.data.mainFeatures.displaySize}</p>
<p class="card-text">Sensors : ${storeSensorName}</p>
</div>
</div>
</div>
</div>
`;
// inject release Date
const container = document.getElementById('details-body');
const p = document.createElement('p');
if (data.data.releaseDate) {
p.innerText = `Release Information:${data.data.releaseDate}`;
container.appendChild(p);
} else {
p.innerText = `Release Information Not Found`;
container.appendChild(p);
}
});
}
//search button action form
document.getElementById('search-btn').addEventListener('click', function() {
clearSearchResult('show-details');
clearSearchResult('show-mobile');
displayHandle('none', 'error-message');
displayHandle('none', 'show-all-Button')
// get search field
const searchBox = document.getElementById('search-box');
const searchData = searchBox.value;
fetch(`https://openapi.programming-hero.com/api/phones?search=${searchData}`)
.then(res => res.json())
.then(data => {
if (data.data.length != 0) {
displayMobile(data.data)
} else {
displayHandle('block', 'error-message');
}
})
// clear search field
searchBox.value = ' ';
})
const displayMobile = (mobiles) => {
// console.log(phones);
const showMobile = document.getElementById('show-mobile');
let count = 0;
for (const mobile of mobiles) {
if (count < 20) {
const div = document.createElement('div');
div.classList.add("col");
div.innerHTML = `
<div class="card d-flex align-items-center">
<img src=${mobile.image} class="card-img-top w-50 h-50" alt="...">
<div class="card-body">
<h5 class="card-title">Model : ${mobile.phone_name}</h5>
<p class="card-text">Brand : ${mobile.brand}</p>
<button class="card-button" onclick="allDetails('${mobile.slug}')">Details</button>
</div>
</div>
`;
showMobile.appendChild(div);
} else {
displayHandle('block', 'show-all-Button');
}
count++;
}
}
<!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>
<!-- connection with bootstrap -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<style>
.display-handle {
display: none;
color: rgb(231, 49, 49);
}
.card-button {
color: white;
background-color: brown;
border-radius: 10px;
}
</style>
</head>
<body>
<!-- search box section
----------------->
<section class="container w-75 mx-auto pt-5">
<h5 id="error-message" class="display-handle text-center">Not found..! Press authentic data...</h5>
<h3 class="text-center bg-dark text-white">Mobile Maya</h3>
<div class="input-group mb-3">
<input id="search-box" type="text" class="form-control" placeholder="Enter your desired mobile " aria-label="" aria-describedby="button-addon2">
<button class="btn btn-success" type="button" id="search-btn">Search</button>
</div>
</section>
<!-- display products details -->
<section id="show-details" class="container">
<!-- inject details here -->
</section>
<!-- search result display section
---------------------------------->
<section class="container pt-5">
<div id="show-mobile" class=" row row-cols-1 row-cols-md-3 g-4">
</div>
</section>
<!-- show all button -->
<section class="container">
<div class="d-flex justify-content-center">
<button style="color: white; background-color:tomato; border-radius: 8px;" id="show-all-Button" class="display-handle" type="button" class="btn btn-primary">Show All</button>
</div>
</section>
<!-- connection with js
----------- -->
<script src="js/app.js"></script>
<!-- connection with bootstrap script -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
</body>
</html>
It is because of this line, you don't really clear the search field :
// clear search field
searchBox.value = ' ';
You should either assign the empty string value '' or call the method trim() on searchData.
I have this bootstrap layout, which is a container inside an accordion-item. There is a dropdown button, containing messages and there's another button that opens a modal to insert a new message and display it in the dropdown list, using localStorage.
When the page loads or is refreshed, I can select the dropdown item normally, but when I add the item, it actually shows up in the list, but I can't select it nor any other item and the selected item is kept there, until the page is reloaded again.
I can still add more messages using the modal and they will still show up in the list.
There's no error in the console to guide me.
The JavaScript code is located at the bottom of the code.
Demonstrating video: https://www.youtube.com/watch?hd=1&v=fhNfpOaJbGs
HTML:
<div class="container">
<div class="row justify-content-center">
<div class="col-4">
<div class="dropdown">
<button class="btn btn-secondary" type="button" id="ddMensagens" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Selecione Mensagem
</button>
<div class="dropdown-menu scrollable-menu" id="mensagem" aria-labelledby="ddMensagens">
</div>
</div>
</div>
<div class="col-4">
<button type="button" class="btn btn-outline-success btn-sm" data-bs-toggle="modal" data-bs-target="#staticBackdrop" title="Clique para cadastrar uma nova mensagem">
Cadastrar Nova Mensagem
</button>
<div class="modal fade" id="staticBackdrop" data-bs-backdrop="false" data-bs-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered"">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="modalMensagem">Nova Mensagem</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Fechar"></button>
</div>
<div class="modal-body">
<div class="container">
<div class="row">
<div class="col">
<div class="form-group">
<label for="txtMensagem">Digite sua mensagem</label>
<input type="text" class="form-control" id="txtMensagem" placeholder="Mensagem" style="width: 400px">
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger" data-bs-dismiss="modal">Fechar</button>
<button type="button" class="btn btn-primary" id="saveMensagem" onclick="addItem();" data-bs-dismiss="modal">Salvar</button>
</div>
</div>
</div>
</div>
</div>
<br />
</div>
</div>
JavaScript:
<script defer>
function updateSelect() {
const select = document.querySelector('#mensagem');
const oldMessage = JSON.parse(localStorage.getItem('mensagem')) || false;
if (oldMessage) {
select.innerHTML = '';
oldMessage.forEach(element => {
select.innerHTML += `<button class='dropdown-item' type='button'>${element}</option>`
});
} else {
localStorage.setItem('mensagem', '[]')
}
}
function addItem() {
const text = document.getElementById('txtMensagem').value;
const database = JSON.parse(localStorage.getItem('mensagem')) || [];
if (database && text.length > 3) {
const repetido = database.find(item => item === text.value);
if (!repetido) {
const novasMensagens = [...database, text];
localStorage.setItem('mensagem', JSON.stringify(novasMensagens))
updateSelect();
text.value = ''
}
}
}
updateSelect()
</script>
LINKS:
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap#5.0.0-beta1/dist/css/bootstrap.min.css" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous">
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" type="text/javascript"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.0.0-beta1/dist/js/bootstrap.bundle.min.js" integrity="sha384-ygbV9kiqUc6oa4msXn9868pTtWMgiQaeYH7/t7LECLbyPA2x65Kgf80OJFdroafW" crossorigin="anonymous"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
Had to change in order to solve my issue (which I still don't know):
function getMensagem() {
let valueMensagem = document.getElementById('mensagem').value;
if (valueMensagem != 0) {
$('select option:selected').each(function (e) {
$('#ContentPlaceHolder1_ddMensagensCopy').val(valueMensagem);
});
}
else {
$('#ContentPlaceHolder1_ddMensagensCopy').val('');
}
}
function updateSelectMensagem() {
const select = document.querySelector('#mensagem');
const oldMessage = JSON.parse(localStorage.getItem('mensagem')) || false;
if (oldMessage) {
select.innerHTML = "<option value='0'> Selecione uma mensagem </option>";
oldMessage.forEach(element => {
select.innerHTML += `<option class='dropdown-item'>${element}</option>`
});
} else {
localStorage.setItem('mensagem', '[]')
}
}
function addItemMensagem() {
const text = document.getElementById('txtMensagem').value;
const database = JSON.parse(localStorage.getItem('mensagem')) || [];
if (database && text.length > 3) {
const repetido = database.find(item => item === text.value);
if (!repetido) {
const novasMensagens = [...database, text];
localStorage.setItem('mensagem', JSON.stringify(novasMensagens))
updateSelectMensagem();
text.value = ''
}
}
}
updateSelectMensagem()
<div class="col-4">
<div class="dropdown-boleto">
<select id="mensagem" class="btn btn-secondary select">
<option value="0">Selecione uma mensagem</option>
</select>
</div>
</div>
.select option {
background: white;
color: #212529;
}
In fact, everything works as expected
https://codesandbox.io/s/shy-cookies-1xt96?file=/index.html
I have an ul (unordered list) which contains several li (list items) when I click on the + sign left to li, console log should output "description expanded" For the two first li, it works just fine, but not for any other li's. The html code by default includes only 2 li's. any li after that is added through the form. However, any new li does not produce the console log output. Which means they're not responding to clicks. I'm using jQuery to listen for click events. When clicking on the icon.
project link http://pctechtips.org/apps/todo/
//variables
//todoList array to hold (title, description) for every todo tiem
var todoList = []; //{title: "value", desc: "value"},
var numId = 2; //num is for desc paragraph id eg <p id="plus-3">
/*
* Script main body
*/
$(document).ready(function() {
//hide form when doc loads first time
$("#submit-form").hide();
//hide list item description <p>
$(".item-desc").hide();
//listener for show hide form functionality
$("#add-todo").click(function() {
toggleForm();
return false; //return false to prevent page reload
});
//listener for add new item form (submit button)
$(".btn").click(function() {
console.log("submit item");
addToList();
});
//listener for expanding li description
$(".plus").click(function() {
console.log("description expanded")
var plusId = $(this).attr("id"); //grabbing id of plus sign
showDescription(plusId);
return false;
});
});
//functionality for show / hide form
function toggleForm() {
if($("#submit-form").is(":hidden")) {
console.log("form exapnded");
$("#submit-form").show("slow");
$("#form-icon").removeClass("fa-plus-square-o");
$("#form-icon").addClass("fa-minus-square-o");
}
else {
console.log("form hidden");
$("#submit-form").hide("slow");
$("#form-icon").removeClass("fa-minus-square-o");
$("#form-icon").addClass("fa-plus-square-o");
}
}
//add new item to todo list items
function addToList() {
numId++;
//getting data from input fields
var todoTitle = $("#todo-title").val();
var todoDesc = $("#todo-desc").val();
//checking user input
if(todoTitle == "" || todoDesc == "") {
alert("fill in all fields!");
}
else {
console.log(todoTitle + "\n" + todoDesc);
//adding values to array
todoList.push({title: todoTitle, desc: todoDesc},);
//adding new li to ul
var ul = document.getElementById("list");
var li = document.createElement("li");
$(li).addClass("list-group-item justify-content-between align-items-center");
$(li).append(($('<i id="plus-'+numId+'" class="plus fa fa-plus-square-o left" aria-hidden="true"></i>')));
$(li).append(($('<span class="left marg-left">'+todoTitle+'</span>')));
$(li).append(($('<i class="fa fa-trash-o right" aria-hidden="true"></i>')));
$(li).append(($('<i class="fa fa-pencil right marg-right pr-2" aria-hidden="true"></i>')));
ul.appendChild(li);
}
}
//expanding description under for each todo
function showDescription(plusId) {
//getting the number from id
var num = plusId.substring(plusId.indexOf("-")+1);
//checking for hide / show description paragraph
if($("#desc-"+num).is(":hidden")) {
$("#desc-"+num).show("slow");
$("#desc-"+num).removeClass("fa-plus-square-o");
$("#desc-"+num).addClass("fa-minus-square-o");
}
else {
$("#desc-"+num).hide("slow");
$("#desc-"+num).removeClass("fa-minus-square-o");
$("#desc-"+num).addClass("fa-plus-square-o");
}
}
index.html
<!DOCTYPE html>
<html>
<head>
<title>TodoList App</title>
<!-- bootstrap, fontawsome cdn -->
<link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
<!-- local stylesheet -->
<link rel="stylesheet" type="text/css" href="css/style.css">
<!-- jquery cdn -->
<script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
<!-- local javascript -->
<script type="text/javascript" src="js/todo.js"></script>
</head>
<body>
<!-- navbar -->
<nav class="navbar navbar-expand-lg navbar-drak bg-dark mb-4">
<a class="navbar-brand" href="#"><i class="fa fa-thumb-tack" aria-hidden="true"></i> Todo<strong>List</strong></a>
</nav>
<!-- /navbar -->
<!-- todoList -->
<div class="container">
<div class="add-item text-white text-center col-sm-12 col-md-10 col-lg-8 mb-4">
<a id="add-todo" class="new-todo text-white text-center" href=""><i id="form-icon" class="fa fa-plus-square-o" aria-hidden="true"></i> Enter new todo item</a>
<div id="submit-form" class="form-hide text-center col-sm-12 col-md-12 col-lg-8">
<form class="">
<div class="form-group">
<input type="text" class="form-control" id="todo-title" placeholder="Todo Title" value="Pay Car Insurance">
</div>
<div class="form-group">
<input type="text" class="form-control" id="todo-desc" placeholder="Todo Description" value="This is to pay car insurance">
</div>
<button type="button" class="btn btn-primary btn-lg col-12">Submit Todo</button>
</form>
</div>
<!-- horizontal line -->
<hr>
<!-- list items -->
<h1 class="heading-4">Todo List Items</h1>
<ul id="list" class="list-group mt-4 pb-4">
<li class="list-group-item justify-content-between align-items-center">
<i id="plus-1" class="plus fa fa-plus-square-o left" aria-hidden="true"></i>
<span class="left marg-left">Pay Car Insurance </span>
<i class="fa fa-trash-o right" aria-hidden="true"></i>
<i class="fa fa-pencil right marg-right pr-2" aria-hidden="true"></i>
</li>
<p id="desc-1" class="item-desc bg-white">Helloooooooooo this is description</p>
<li class="list-group-item justify-content-between align-items-center">
<i id="plus-2" class="plus fa fa-plus-square-o left" aria-hidden="true"></i>
<span class="left marg-left">Pay Car Insurance </span><i class="fa fa-trash-o right" aria-hidden="true"></i>
<i class="fa fa-pencil right marg-right pr-2" aria-hidden="true"></i>
</li>
<p id="desc-2" class="item-desc bg-white">Helloooooooooo this is description</p>
</ul>
</div>
</div>
</body>
</html>
ok for dynamically added elements the even listener needs to be defined at the root / document level. so
$("class").on("click", function() { });
will not work. It needs to be
$(document).on("click", ".class", function() { });
Thanks to "MADDY BLACKLISTED" above pointed me in right directions
Use
$(document).on("click", ".plus", function() {
console.log("description expanded")
var plusId = $(this).attr("id"); //grabbing id of plus sign
showDescription(plusId);
return false;
});
Instead of having a static click listener defined at document load.