For some reasons unbeknownst to me, the events for elements I created dynamically fires multiple times (mulitplying itself by three each time).
Those, I added manually fire only ones. As you can see, I have some console.log() which I use to track if the function was called multiple times or it was the event handler that fired multiple times. Also added event.stopPropagation(), still fires.
here is my html code
{% extends "mail/layout.html" %}
{% load static %}
{% block body %}
<h2>{{ request.user.email }}</h2>
<button class="btn btn-sm btn-outline-primary" id="inbox">Inbox</button>
<button class="btn btn-sm btn-outline-primary" id="compose">Compose</button>
<button class="btn btn-sm btn-outline-primary" id="sent">Sent</button>
<button class="btn btn-sm btn-outline-primary" id="archived">Archived</button>
<a class="btn btn-sm btn-outline-primary" href="{% url 'logout' %}">Log Out</a>
<hr>
<div id="emails-view">
</div>
<div id="mail-template">
</div>
<div id="compose-view">
<h3>New Email</h3>
<form id="compose-form" action="#">
<div class="form-group">
From: <input disabled class="form-control" value="{{ request.user.email }}">
</div>
<div class="form-group">
To: <input id="compose-recipients" class="form-control" placeholder="Recipient">
</div>
<div class="form-group">
<input class="form-control" id="compose-subject" placeholder="Subject" value="HOMES">
</div>
<p><textarea class="form-control" id="compose-body" placeholder="Body"></textarea></p>
<p><input value="Send" type="submit" class="btn btn-primary" id='compose-submit'/></p>
<!--<p><button class="btn btn-primary" id="compose-submit">Send</button></p>-->
</form>
</div>
{% endblock %}
{% block script %}
<script src="{% static 'mail/index.js' %}" type='text/javascript'></script>
{% endblock %}
here is my Javascript file
function check_parent(e){
return e.id || e.parentNode.id || e.parentNode.parentNode.id;
}
function read_unread(mail_id, action){
let todo = (action == 'read') ? true : false;
fetch(`emails/${mail_id}`,{
method: 'PUT',
body: JSON.stringify({
read: todo
})
})
}
// This one has an event that fires multiple times
function open_email(mail_id) {
document.querySelector('#emails-view').style.display = 'none';
document.getElementById('mail-template').style.display = 'block';
document.querySelector('#compose-view').style.display = 'none';
let mail_template = document.getElementById('mail-template')
fetch(`emails/${mail_id}`).then(
response => response.json())
.then(result =>
{
let mail = `<div><strong>From</strong>: ${result['sender']}</div>`;
mail += `<div><strong>To</strong>: ${result['recipients']}</div>`;
mail += `<div><strong>Subject</strong>: ${result['subject']}</div>`;
mail += `<div><strong>Timestamp</strong>: ${result['timestamp']}</div>`;
mail += '<p><input value="Reply" type="submit" class="btn btn-sm btn-outline-primary" id="reply"/></p>'
mail += '<hr>'
mail += `${result['body']}`
mail_template.innerHTML = mail;
mail_template.addEventListener('click', e => {
if (e.target.id === 'reply'){
e.stopPropagation();
e.preventDefault();
let tops = `${result['sender']}`;
compose_email(tops);
}
});
})
read_unread(mail_id, 'read');
}
document.addEventListener('DOMContentLoaded', function() {
// Use buttons to toggle between views
document.querySelector('#inbox').addEventListener('click', () => load_mailbox('inbox'));
document.querySelector('#sent').addEventListener('click', () => load_mailbox('sent'));
document.querySelector('#archived').addEventListener('click', () => load_mailbox('archive'));
document.querySelector('#compose').addEventListener('click', compose_email);
// By default, load the inbox
load_mailbox('inbox');
});
function compose_email(receiver='') {
// Show compose view and hide other views
document.querySelector('#emails-view').style.display = 'none';
document.getElementById('mail-template').style.display = 'none';
document.querySelector('#compose-view').style.display = 'block';
// Clear out composition fields
document.querySelector('#compose-recipients').value = receiver;
document.querySelector('#compose-subject').value = '';
document.querySelector('#compose-body').value = '';
}
document.querySelector('#compose-form').addEventListener('submit', (e) => {
document.querySelector('#compose-recipients').value;
let recipients = document.querySelector('#compose-recipients').value;
let subject = document.querySelector('#compose-subject').value;
let body = document.querySelector('#compose-body').value;
e.preventDefault();
// Send mail to the backend
fetch('/emails', {
method: 'POST',
body: JSON.stringify({
recipients: recipients,
subject: subject,
body: body
})
}).then(response => response.json())
.then(result => {console.log(result)})
load_mailbox('sent')
});
// This one has an event that fires multiple times
function load_mailbox(mailbox) {
console.log('first');
let emails_view = document.querySelector('#emails-view')
// Show the mailbox and hide other views
document.querySelector('#emails-view').style.display = 'block';
document.getElementById('mail-template').style.display = 'none';
document.querySelector('#compose-view').style.display = 'none';
// Show the mailbox name
document.querySelector('#emails-view').innerHTML = `<h3>${mailbox.charAt(0).toUpperCase() + mailbox.slice(1)}</h3>`;
// Retrieve users sent emails
fetch(`/emails/${mailbox}`).then(response => response.json())
.then(emails => {
emails.forEach( email => {
let div = document.createElement('div')
div.className = 'mail-boxs';
//div.href = `emails/${email['id']}`;
if (email['read'] === true){
div.style.backgroundColor = '#DCDCDC';
}else {
div.style.backgroundColor = 'white';
}
div.id = `${email['id']}`;
div.style.border = 'solid 1px gray';
div.style.padding = '8px';
let email_sub = document.createElement('span')
email_sub.className = 'mail-boxs';
email_sub.innerHTML = `<strong class="mail-boxs">${email['sender']}</strong> ${email['subject']}`
let time_stamp = document.createElement('span')
time_stamp.innerHTML = `${email['timestamp']}`
time_stamp.className = 'mail-boxs';
time_stamp.style.float = 'right';
div.appendChild(email_sub);
div.appendChild(time_stamp);
emails_view.appendChild(div)
emails_view.addEventListener('click', e => {
console.log('second');
if (e.target.className == 'mail-boxs'){
let mail_id = check_parent(e.target);
open_email(parseInt(mail_id))
}
})
});
});
}
Edit, parameters get [object MouseEvent] as values of default parameters.
Here is the full code. It is a bit longer, but I have added multi-line comment to the relevant parts vis-à-vis the arbitrary values passed to compose_mail.
let emails_view = document.querySelector('#emails-view')
let mail_template = document.getElementById('mail-template')
function check_parent(e){
return e.id || e.parentNode.id || e.parentNode.parentNode.id;
}
// Read or unread an email
function read_unread(mail_id, action){
let todo = (action == 'read') ? true : false;
fetch(`emails/${mail_id}`,{
method: 'PUT',
body: JSON.stringify({
read: todo
})
})
}
// Archive or unarchive an email
function archive(mail_id, action){
let todo = (action == 'archive') ? true : false;
fetch(`emails/${mail_id}`,{
method: 'PUT',
body: JSON.stringify({
archived: todo
})
})
}
function open_email(mail_id) {
document.querySelector('#emails-view').style.display = 'none';
document.getElementById('mail-template').style.display = 'none';
setTimeout(() =>
{
document.getElementById('mail-template').style.display = 'block';
},
300
);
document.querySelector('#compose-view').style.display = 'none';
fetch(`emails/${mail_id}`).then(
response => response.json())
.then(result =>
{
let mail = `<div><strong>From</strong>: ${result['sender']}</div>`;
mail += `<div><strong>To</strong>: ${result['recipients']}</div>`;
mail += `<div><strong>Subject</strong>: ${result['subject']}</div>`;
mail += `<div><strong>Timestamp</strong>: ${result['timestamp']}</div>`;
if (window.mail_title != 'Sent'){
mail += '<p><input value="Reply" type="submit" class="btn btn-sm btn-outline-primary" id="reply"/>'
mail += `<input id="${mail_id}" value="Unread" type="submit" class="btn btn-sm btn-outline-primary read" style="float: right;"/>`
mail += `<input value="" id="${mail_id}" type="submit" class="btn btn-sm btn-outline-primary archive" style="float: right; margin-right: 5px;"/></p>`
}
mail += '<hr>'
mail += `${result['body']}`
mail_template.innerHTML = mail;
declare_results(result);
let arch = document.querySelector('.archive');
if (window.mail_title != 'Sent'){
if (result['archived'] == true) {
arch.value = 'Unarchive';
}else if (arch.value == ""){
arch.value = "Archive";
}
}
})
read_unread(mail_id, 'read');
}
// Declare the returned value from fetch email above
function declare_results(value){
window.results = value;
}
// Event to track if the 'reply' button was clicked
mail_template.addEventListener('click', e => {
if (e.target.id === 'reply'){
window.tops = `${window.results['sender']}`;
window.subject = `${window.results['subject']}`;
window.body = `${window.results['body']}`;
window.date = `${window.results['timestamp']}`;
/*
Here is where I call the function with arguments
which act as expected.
*/
compose_email(window.tops, window.subject, window.body, window.date);
}
else if (e.target.value == 'Unread') {
read_unread(parseInt(e.target.id), 'unread')
e.target.value = 'Read';
}
else if (e.target.value == 'Read') {
read_unread(parseInt(e.target.id), 'read')
e.target.value = 'Unread';
}
else if (e.target.value == 'Unarchive') {
archive(parseInt(e.target.id), 'unarchive')
e.target.value = 'Archive';
}
else if (e.target.value == 'Archive') {
archive(parseInt(e.target.id), 'archive')
e.target.value = 'Unarchive';
}
});
document.addEventListener('DOMContentLoaded', function() {
// Use buttons to toggle between views
document.querySelector('#inbox').addEventListener('click', () => load_mailbox('inbox'));
document.querySelector('#sent').addEventListener('click', () => load_mailbox('sent'));
document.querySelector('#archived').addEventListener('click', () => load_mailbox('archive'));
/*
Here is where I call it without arguments; unfortunately,
all the parameters get mouseevent as values in the compose_mail
function.
*/
document.querySelector('#compose').addEventListener('click', compose_email);
// By default, load the inbox
load_mailbox('inbox');
});
/*
This is the function that receives the argument. The argument
were default argument as shown in the previous
code (there, one argument [receiver]). Because the parameters get
these mouse event as argument, I used If-else statement to
update the values if none were given (the expected parameters are not
string) before using them.
*/
function compose_email(receiver, subject, body, date) {
// Show compose view and hide other views
document.querySelector('#emails-view').style.display = 'none';
document.getElementById('mail-template').style.display = 'none';
document.querySelector('#compose-view').style.display = 'block';
// Clear out composition fields
if (typeof receiver != 'string') {
document.querySelector('#compose-recipients').value = '';
document.querySelector('#compose-subject').value = '';
document.querySelector('#compose-body').value = '';
} else {
document.querySelector('#compose-recipients').value = receiver;
document.querySelector('#compose-subject').value = 'Re: ' + subject;
document.querySelector('#compose-body').value = `On ${date} ${receiver} wrote: ` + body;
}
}
document.querySelector('#compose-form').addEventListener('submit', (e) => {
document.querySelector('#compose-recipients').value;
let recipients = document.querySelector('#compose-recipients').value;
let subject = document.querySelector('#compose-subject').value;
let body = document.querySelector('#compose-body').value;
e.preventDefault();
// Send mail to the backend
fetch('/emails', {
method: 'POST',
body: JSON.stringify({
recipients: recipients,
subject: subject,
body: body
})
}).then(response => response.json())
.then(result => {console.log(result)})
load_mailbox('sent')
});
function load_mailbox(mailbox) {
// Show the mailbox and hide other views
document.querySelector('#emails-view').style.display = 'block';
document.getElementById('mail-template').style.display = 'none';
document.querySelector('#compose-view').style.display = 'none';
// Show the mailbox name and get its textContent
document.querySelector('#emails-view').innerHTML = `<h3 class="mail-title">${mailbox.charAt(0).toUpperCase() + mailbox.slice(1)}</h3>`;
window.mail_title = document.querySelector('.mail-title').textContent
// Retrieve user's sent emails
fetch(`/emails/${mailbox}`).then(response => response.json())
.then(emails => {
emails.forEach( email => {
let div_top = document.createElement('div')
let div = document.createElement('div')
div.className = 'mail-boxs';
if (window.mail_title != 'Sent'){
if (email['read'] === true){
div.style.backgroundColor = '#DCDCDC';
}else {
div.style.backgroundColor = 'white';
}
}
div.id = `${email['id']}`;
div.style.border = 'solid 1px gray';
div.style.padding = '8px';
let email_sub = document.createElement('span')
email_sub.className = 'mail-boxs';
email_sub.innerHTML = `<strong class="mail-boxs">${email['sender']}</strong> ${email['subject']}`
let time_stamp = document.createElement('span')
time_stamp.innerHTML = `${email['timestamp']}`
time_stamp.className = 'mail-boxs';
time_stamp.style.float = 'right';
div_top.className = 'move';
div.appendChild(email_sub);
div.appendChild(time_stamp);
div_top.append(div);
emails_view.appendChild(div_top);
});
});
}
// Event to get the div element (message) that
// was clicked
emails_view.addEventListener('click', e => {
if (e.target.className == 'mail-boxs'){
let mail_id = check_parent(e.target);
open_email(parseInt(mail_id))
}
})
The command emails_view.addEventListener('click', e => { is inside a foreach loop.
Based on the next lines, you inspect if the class name is 'mail-boxs' which was applied to the div element, should the EventListener be added to div instead? Like this:
div.addEventListener('click', e => {
The mail_template.addEventListener('click', e => { seems to add an EventListener each time you open an email since you are replacing only the InnerHtml.
In this case i suggest you remove the previous click EventListener, before adding a new one but it seems i little tricky with anonymous functions:
Removing an anonymous event listener
How to remove all listeners in an element?
Related
I put a snippet to make the idea better.
Type any character in the Find field, in the console you will see the log of the dataAdd object, if now we change the value of the select Department and type any character again, the log becomes double, with the value of Department previous and current one, and if you repeat this, each time you add the value in the log.
I need the passed value to send it on the server side to filter the search by department but this way it can't work.
Can you help me, I'm going crazy
const $ = id => document.getElementById(id);
let ajaxInProgress = false;
const ajaxGet = async (elab, q, dataAdd) => {
if (ajaxInProgress) return;
ajaxInProgress = true;
let url;
if (dataAdd != null) {
url = "/utility/cerca/getLiveSearch/" + elab + "/" + q + "/" + btoa(JSON.stringify(dataAdd));
} else {
url = "/utility/cerca/getLiveSearch/" + elab + "/" + q;
}
let response = await fetch(url),
data = await response.text();
if (response.ok) {
ajaxInProgress = false;
return data;
} else if (response.status === 404) {
ajaxInProgress = false;
return false;
} else {
ajaxInProgress = false;
return "Error: " + response.status;
}
};
const liveSearch = (id, elCont, elab, dataAdd) => {
const el = document.querySelector("input" + id);
// If I can't find the execution block element
if (!el) return !1;
el.classList.add("liveSearch");
el.parentElement.classList.add("position-relative");
el.setAttribute("autocomplete", "off");
// If the item doesn't already exist, I create it
if (!$(elCont)) {
// Appendo elemento al campo input
let lsContainer = document.createElement("div");
lsContainer.id = elCont;
lsContainer.classList.add("liveSearchContent", "shadow", "bg-white", "border", "border-2", "rounded-2", "position-absolute", "overflow-auto", "mt-1", "d-none");
el.after(lsContainer);
}
// To KeyUp
el.addEventListener("keyup", ls => {
// I get the typed value
let q = el.value,
innerResult = document.querySelector("#" + elCont);
if (q != "") {
// Log data send from input field
console.log(dataAdd);
ajaxGet(elab, q, dataAdd).then(html => {
// Resize liveSearch
resizeLiveSearch();
// I hang the results
innerResult.innerHTML = html + ' - ' + q;
// Hide/Show liveSearch
ls.target.value.length < 1 ? innerResult.classList.add("d-none") : innerResult.classList.remove("d-none");
});
} else {
// Hide/Show liveSearch
ls.target.value.length < 1 ? innerResult.classList.add("d-none") : innerResult.classList.remove("d-none");
}
});
el.addEventListener("focus", el => {
el.target.closest("div").querySelector(".liveSearchContent").classList.remove("d-none");
});
// When I resize the window, I resize the livesearch
window.onresize = resizeLiveSearch;
document.addEventListener("click", e => {
let box = document.querySelector(".liveSearchContent");
if (box) {
if (box.contains(e.target) || !e.target.classList.contains("liveSearch")) {
handleBlur();
} else {
return false;
}
}
});
};
let resizeLiveSearch = () => {
document.querySelectorAll(".liveSearch").forEach(e => {
e.closest("div").querySelector(".liveSearchContent").style.width = e.offsetWidth + "px";
});
};
let handleBlur = () => {
document.querySelectorAll(".liveSearchContent").forEach(el => {
el.classList.add("d-none");
});
};
// -- Piece of code that interacts with liveSearch
document.addEventListener("DOMContentLoaded", () => {
let idReparto = $("id_reparto"),
idEvento = $("id_evento"),
evento = $("evento"),
reparto = idReparto.value;
idReparto.addEventListener("change", el => {
console.clear();
idEvento.value = evento.value = "";
reparto = el.target.value;
liveSearch("#evento", "searchEventoRicerca", "evento_reparto", { reparto: reparto });
});
liveSearch("#evento", "searchEventoRicerca", "evento_reparto", { reparto: $("id_reparto").value });
$("searchEventoRicerca").addEventListener("click", el => {
let elt = el.target,
evento2 = "";
if (elt.matches(".srel")) {
evento2 = elt.getAttribute("id");
el.preventDefault();
elt.blur();
evento.value = elt.text;
idEvento.value = evento2;
pdvEvento.value = idPdv.value = "";
// Mostro pdv_evento e occulto pdv
pdvEvento.parentElement.classList.remove("d-none");
console.log(evento2);
}
});
});
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.2.2/dist/css/bootstrap.min.css" rel="stylesheet"/>
<form class="mb-5 mt-2 mx-2">
<div class="row g-3">
<input type="hidden" id="id_evento" name="id_evento" value="">
<div class="col-md-3">
<label for="id_reparto" class="form-label">Department</label>
<select class="form-select" id="id_reparto" name="id_reparto" title="id_reparto">
<option value="1">Dep 1</option>
<option value="3">Dep 3</option>
<option value="2" selected="selected">Dep 2</option>
</select>
</div>
<div class="col-md-6">
<label for="evento" class="form-label">Find</label>
<input type="text" class="form-control liveSearch" placeholder="Type any character" id="evento" name="evento" value="" autocomplete="off">
<div id="searchEventoRicerca" class="liveSearchContent shadow bg-white border border-2 rounded-2 position-absolute overflow-auto mt-1 d-none"></div>
</div>
</div>
</form>
I am trying to create a "library" where you can add books by yourself and they also get a "remove" button that removes the book and a "change status" button that changes the status of the book has been read or not.
Everything works fine except the "change status" button, which only lets the user change the status of every other book. for example if you add 4 books you would only be able to change book 2 and 4, if you add 5 books you can only change books 1,3,5 instead of working for all of them.
const myLibrary = [];
function Book(title, author, pages, read) {
this.title = title;
this.author = author;
this.pages = pages;
this.read = read;
}
// Prototype functions
Book.prototype.info = function() {
return `${this.title} by ${this.author}, ${this.pages} pages, ${this.read}`;
};
Book.prototype.changeStatus = function() {
if ((this.read) == 'Already read') {
this.read = 'Haven\'t read yet';
} else if ((this.read) == 'Haven\'t read yet') {
this.read = 'Already read';
}
}
// Add a new book to the array
function addBookToLibrary(title, author, pages, read) {
const newBook = new Book(title, author, pages, read);
myLibrary.push(newBook);
}
// Examples
addBookToLibrary('Title', 'Author', 251, 'Already read');
addBookToLibrary('The book', 'John Smith', 511, 'Already read');
addBookToLibrary('Rich quick', 'Robert', 2541, 'Haven\'t read yet');
addBookToLibrary('How to code', 'Odin', 221, 'Already read');
const books = document.querySelector('.books');
// Display examples for testing
for (let i = 0; i < myLibrary.length; i++) {
console.log(myLibrary[i].info());
const container = document.createElement('div');
books.appendChild(container);
const div = document.createElement('div');
div.textContent = myLibrary[i].info();
container.appendChild(div);
}
// Display submitted books
function displayBooks() {
const index = myLibrary.length - 1;
const container = document.createElement('div');
container.classList.add('bookContainer');
container.setAttribute('data-', `${index}`);
books.appendChild(container);
const div = document.createElement('div');
div.classList.add('aBook');
div.setAttribute('data-', `${index}`);
div.textContent = myLibrary[index].info();
container.appendChild(div);
const btn1 = document.createElement('button');
btn1.classList.add('remover');
btn1.setAttribute('data-', `${index}`);
btn1.textContent = 'Remove';
container.appendChild(btn1);
createRemover();
const btn2 = document.createElement('button');
btn2.classList.add('changer');
btn2.setAttribute('data-', `${index}`);
btn2.textContent = 'Change Status';
container.appendChild(btn2);
createChanger();
}
const form = document.querySelector('form');
form.style.display = "none"
// Event handler for new book button
function toggleFormOn() {
form.style.display = "block"
}
// Event handler for submit form button
function toggleFormOff(event) {
const createBook = [];
const inputs = document.querySelectorAll('input');
const select = document.querySelector('select');
inputs.forEach((input) => {
console.log(input.value);
createBook.push(input.value);
input.value = '';
})
console.log(select.value);
createBook.push(select.value);
form.style.display = "none";
console.log(createBook);
addBookToLibrary(...createBook)
for (let i = 0; i < myLibrary.length; i++) {
console.log(myLibrary[i].info());
}
displayBooks();
event.preventDefault();
}
// Basic Event listeners
const newBtn = document.querySelector('#newBtn');
newBtn.addEventListener('click', toggleFormOn);
const subBtn = document.querySelector('#sub');
subBtn.addEventListener('click', toggleFormOff);
// Event listening and handling
function createRemover() {
const removeBtn = document.querySelectorAll('.remover');
removeBtn.forEach((button) => {
button.addEventListener('click', function(e) {
const placement = button.getAttribute('data-');
const bookDiv = document.querySelectorAll('.bookContainer');
bookDiv.forEach((div) => {
if (div.firstElementChild.textContent == (myLibrary[placement].info())) {
div.remove();
}
})
})
})
}
function createChanger() {
const changeBtn = document.querySelectorAll('.changer');
changeBtn.forEach((button) => {
button.addEventListener('click', function(e) {
const placement = button.getAttribute('data-');
const bookDiv = document.querySelectorAll('.bookContainer');
bookDiv.forEach((div) => {
if (div.firstElementChild.textContent == (myLibrary[placement].info())) {
myLibrary[placement].changeStatus();
div.firstElementChild.textContent = myLibrary[placement].info();
}
})
})
})
}
<div class="books">
</div>
<button id="newBtn">NEW BOOK</button>
<form>
<ul>
<li>
<label for="bookTitle">Title</label>
<input type="text" id="bookTitle" name="bookTitle">
</li>
<li>
<label for="bookAuthor">Author</label>
<input type="text" id="bookAuthor" name="bookAuthor">
</li>
<li>
<label for="bookPages">Pages</label>
<input type="text" id="bookPages" name="bookPages">
</li>
<li id="heading">Did you read it?</li>
<li>
<select name="bookRead" id="bookRead">
<option value="Already read">Read</option>
<option value="Haven't read yet">Didn't read</option>
</select>
</li>
<li class="btn">
<button type="submit" id="sub">Submit</button>
</li>
</ul>
</form>
I tried changing a lot of stuff: tweaking the html structure, modifying the createChanger() and also displayBooks(). Nothing seems to work since I can't identify the issue that is causing it to only change every other book and not every single book.
I have a firebase database and I want to get all the chats and add an HTML element to all of them. Each div has a password input and a button. I made a forEach() loop but it only makes an event listener for the last one. I'm not very experienced so it is probably a simple problem. This is my code:
get(ref(db, "chats")).then((snapshot)=>{
snapshot.forEach((child)=>{
var html = "";
var childName = child.val().chatname;
var childPassword = child.val().chatpassword;
console.log(childName);
html += `
<li>
<div class="chat-login-container">
<h3>`+childName+`</h3>
<input id="`+childName+`-input" type="password" placeholder="Enter Chat Password">
<button id="`+childName+`-button" class="login">Enter</button>
</div>
</li>
`;
messages.innerHTML += html;
var button = document.getElementById(childName+"-button");
var passwordInput = document.getElementById(childName+"-input");
button.addEventListener("click", ()=>{
get(ref(db, "chats/"+childName)).then((snapshot) =>{
if(passwordInput==childPassword){
chat = childName;
console.log("Logging in to "+childName);
messages.innerHTML = "";
start();
}else{
window.alert("Incorrect password, please try again");
}
}).catch((error)=>{
console.log(error);
});
});
});
}).catch((error)=>{
console.log(error);
});
Because, you are capturing the button by ID. It will capture the last ID recorded.
Instead use querySelector.
Try below, which is not a proper code, you need to tweak based on error messages.
get(ref(db, "chats")).then((snapshot) => {
snapshot.forEach((child) => {
var html = "";
var childName = child.val().chatname;
var childPassword = child.val().chatpassword;
console.log(childName);
html += `
<li>
<div class="chat-login-container">
<h3>`+ childName + `</h3>
<input type="hidden" class="childname" value="`+ childname + `">
<input type="hidden" class="childPassword" value="`+ childPassword + `">
<input id="`+ childName + `-input" class="password" type="password" placeholder="Enter Chat Password">
<button id="`+ childName + `-button" class="login">Enter</button>
</div>
</li>
`;
messages.innerHTML += html;
});
const chatLoginContainers = document.querySelectorAll('.chat-login-container');
chatLoginContainers.forEach((element) => {
const loginBtn = element.querySelector('.login');
loginBtn.addEventListener('click', (event) => {
const childNameEle = event.target.closest('li').querySelector('.childname');
const childPasswordEle = event.target.closest('li').querySelector('.childPassword');
const passwordEle = event.target.closest('li').querySelector('.password');
const childNameVal = childNameEle.value;
const childPasswordVal = childPasswordEle.value;
const passwordVal = passwordEle.value;
get(ref(db, "chats/" + childNameVal)).then((snapshot) => {
if (passwordVal == childPasswordVal) {
chat = childNameVal;
console.log("Logging in to " + childNameVal);
messages.innerHTML = "";
start();
} else {
window.alert("Incorrect password, please try again");
}
}).catch((error) => {
console.log(error);
});
})
});
}).catch((error) => {
console.log(error);
});
i try to make a website with different images and user can give 5=-star-rating to them so i save the images from admin side and place a card into loop to show all the present data from database and with that i also place 5-star- rate to give rating but it saving the data of 1st image only in the card i want it to save rating of each image, i search it everywhere but cant find any solution please help me with this.
This is my HTML page
<div class="container">
<div class="row">
{%for a in ab%}
<div class="col-4">
<div class="card">
<img class="card-img-top" src="{{a.image.url}}">
<div class="card-body">
<h5 class="card-title">{{a.product}}</h5>
<div class="col text-center">
<form class="rate-form" action="" method="POST" id="{{a.id}}">
{% csrf_token %}
<button type="submit" class="fa fa-star fa-3x my-btn" id="first"></button>
<button type="submit" class="fa fa-star fa-3x my-btn" id="second"></button>
<button type="submit" class="fa fa-star fa-3x my-btn" id="third"></button>
<button type="submit" class="fa fa-star fa-3x my-btn" id="fourth"></button>
<button type="submit" class="fa fa-star fa-3x my-btn" id="fifth"></button>
</form>
<br>
<div id="confirm-box"></div>
</div>
</div>
</div>
{%endfor%}
</div>
</div>
This is my java-script
// console.log('hello world')
// get all the stars
const one = document.getElementById('first')
const two = document.getElementById('second')
const three = document.getElementById('third')
const four = document.getElementById('fourth')
const five = document.getElementById('fifth')
// get the form, confirm-box and csrf token
const form = document.querySelector('.rate-form')
const confirmBox = document.getElementById('confirm-box')
// const csrf = document.getElementsByName('CsrfViewMiddleware')
var csrf = $("input[name=csrfmiddlewaretoken]").val();
const handleStarSelect = (size) => {
const children = form.children
console.log(children[0])
for (let i=0; i < children.length; i++) {
if(i <= size) {
children[i].classList.add('checked')
} else {
children[i].classList.remove('checked')
}
}
}
const handleSelect = (selection) => {
switch(selection){
case 'first': {
// one.classList.add('checked')
// two.classList.remove('checked')
// three.classList.remove('checked')
// four.classList.remove('checked')
// five.classList.remove('checked')
handleStarSelect(1)
return
}
case 'second': {
handleStarSelect(2)
return
}
case 'third': {
handleStarSelect(3)
return
}
case 'fourth': {
handleStarSelect(4)
return
}
case 'fifth': {
handleStarSelect(5)
return
}
default: {
handleStarSelect(0)
}
}
}
const getNumericValue = (stringValue) =>{
let numericValue;
if (stringValue === 'first') {
numericValue = 1
}
else if (stringValue === 'second') {
numericValue = 2
}
else if (stringValue === 'third') {
numericValue = 3
}
else if (stringValue === 'fourth') {
numericValue = 4
}
else if (stringValue === 'fifth') {
numericValue = 5
}
else {
numericValue = 0
}
return numericValue
}
if (one) {
const arr = [one, two, three, four, five]
arr.forEach(item=> item.addEventListener('mouseover', (event)=>{
handleSelect(event.target.id)
}))
arr.forEach(item=> item.addEventListener('click', (event)=>{
// value of the rating not numeric
const val = event.target.id
let isSubmit = false
form.addEventListener('submit', e=>{
e.preventDefault()
if (isSubmit) {
return
}
isSubmit = true
// picture id
const id = e.target.id
// value of the rating translated into numeric
const val_num = getNumericValue(val)
$.ajax({
url: '/add_rating/',
type: 'post',
data: {
'csrfmiddlewaretoken': csrf,
'el_id': id,
'val': val_num,
},
success: function(response){
console.log(response)
confirmBox.innerHTML = `<h1>Successfully rated with ${response.score}</h1>`
},
error: function(error){
console.log(error)
confirmBox.innerHTML = '<h1>Ups... something went wrong</h1>'
}
})
})
}))
}
jQuery(document).ready(function($){
$(".btnrating").on('click',(function(e) {
var previous_value = $("#selected_rating").val();
var selected_value = $(this).attr("data-attr");
$("#selected_rating").val(selected_value);
$(".selected-rating").empty();
$(".selected-rating").html(selected_value);
for (i = 1; i <= selected_value; ++i) {
$("#rating-star-"+i).toggleClass('btn-warning');
$("#rating-star-"+i).toggleClass('btn-default');
}
for (ix = 1; ix <= previous_value; ++ix) {
$("#rating-star-"+ix).toggleClass('btn-warning');
$("#rating-star-"+ix).toggleClass('btn-default');
}
}));
});
I'm creating a CRUD page where the user can add, delete and edit text, but I have an issue in updating the text after I select it for edit.
In editText function when I click the edit button the text that was added will pop up inside the input field. When I click on the update button (triggering the updateText function), I can see the text in console log but the corresponding html is not updated.
HTML
<div class="main">
<form>
<input type="text" placeholder="search">
</form>
<ul></ul>
<div>
<input class="add-text" type="text" placeholder="Add Text">
<button id="add">Add</button>
<button id="update">update</button>
</div>
</div>
Javascript
const inputsearch = document.querySelector('form input');
const addInputBtn = document.querySelector('#add');
const update = document.querySelector('#update');
addInputBtn.addEventListener('click', addtext);
function addtext(){
let li = document.createElement('li');
let inputadd = document.querySelector('.add-text');
let addedtext = inputadd.value;
let h1Tag = '<h1 id="text">'+addedtext+'</h1>';
let tags = h1Tag + '<button id="delete">Delete</button><button id="edit">Edit</button>';
if(addedtext == ''){
alert('please add some text');
return;
}else{
li.innerHTML = tags;
document.querySelector('ul').appendChild(li);
}
li.querySelectorAll('#delete')[0].addEventListener('click', deleteText);
li.querySelectorAll('#edit')[0].addEventListener('click', editText);
getlist(li, h1Tag);
inputadd.value = '';
}
function deleteText(e) {
e.target.parentNode.remove();
document.querySelector('.add-text').value = '';
}
function editText(e) {
let currentText = e.target.parentNode.firstChild.textContent;
let currentValue = document.querySelector('.add-text');
currentValue.value = currentText;
getupdate(currentText, currentValue);
}
function getupdate(currentText, currentValue) {
update.addEventListener('click', updateText);
function updateText() {
currentText = currentValue.value
console.log(currentText = currentValue.value);
}
}
function getlist(li, h1Tag) {
inputsearch.addEventListener('keyup', serchText);
function serchText(e) {
let typetext = e.target.value.toLowerCase();
if(h1Tag.toLowerCase().indexOf(typetext) != -1){
li.style.display = 'block';
}else{
li.style.display = 'none';
}
}
}
To solve the issue without changing your overall approach, your edit button click needs to get the corresponding element (not just its textContent) and pass it to your getupdate() function to be updated when your update button is clicked. Relatively minor changes to your current functions:
function editText(e) {
const currentText = e.target.parentNode.firstChild;
const currentValue = document.querySelector('.add-text');
currentValue.value = currentText.textContent;
getupdate(currentText, currentValue);
}
function getupdate(currentText, currentValue) {
update.addEventListener('click', updateText);
function updateText() {
currentText.textContent = currentValue.value;
}
}
There are some other issues with your code, particularly the creation of multiple elements with the same id (which is malformed and will likely become problematic as you add additional features). Following is a snippet that addresses that issue as well as simplifying some of your functions and fixing the search.
const search = document.querySelector('form input');
const input = document.querySelector('.add-text');
const container = document.querySelector('ul');
let items = null;
let currentItem = null;
const searchItems = (event) => {
if (items) {
const s = event.currentTarget.value.toLowerCase();
for (const item of items) {
if (item.firstChild.textContent.toLowerCase().indexOf(s) !== -1) {
item.style.display = 'block';
} else {
item.style.display = 'none';
}
}
}
};
const deleteItem = (event) => {
currentItem = null;
event.currentTarget.parentNode.remove();
};
const editItem = (event) => {
currentItem = event.currentTarget.parentNode.firstChild;
input.value = currentItem.textContent;
};
const updateItem = () => {
if (currentItem) {
currentItem.textContent = input.value;
}
};
const addItem = () => {
let val = input.value
if (val) {
const li = document.createElement('li');
let inner = '<h1 class="text">' + val + '</h1>';
inner += '<button class="delete">Delete</button>';
inner += '<button class="edit">Edit</button>';
li.innerHTML = inner;
container.appendChild(li);
val = '';
currentItem = li.firstChild;
items = document.querySelectorAll('li');
for (let del of document.querySelectorAll('.delete')) {
del.addEventListener('click', deleteItem);
}
for (let edit of document.querySelectorAll('.edit')) {
edit.addEventListener('click', editItem);
}
} else {
alert('please add some text');
return;
}
};
search.addEventListener('keyup', searchItems);
document.querySelector('#add').addEventListener('click', addItem);
document.querySelector('#update').addEventListener('click', updateItem);
<div class="main">
<form>
<input type="text" placeholder="Search">
</form>
<ul></ul>
<div>
<input class="add-text" type="text" placeholder="Add Text">
<button id="add">Add</button>
<button id="update">Update</button>
</div>
</div>