Reputation: 73
I have 2 click listeners set up, these handle showing a modal and removing a note. These listeners use event delegation and is set up on parent container. The Note is rendered afters when users inputs one. The issue is, when user clicks on a note , a modal pops up to show its content but at the same time the remove note listener is also fired. How do I stop next listener from firing when one is handled.
/*Controller.js*/
async function removeNote(ele) {
let noteId = { id: ele.dataset.id };
let deletedNote = await sendAPIRequest("deleteNote", "DELETE", noteId);
let localDeletedNote = deleteNoteFromLocal(deletedNote);
NoteView.removeNotefromView(localDeletedNote);
}
function closeModal(ele) {
let modal = document.querySelector(".modal");
if (!document.querySelector("body").contains(ele)) {
modal.style.display = "none";
} else {
modal.style.display = "none";
}
}
function showModal(ele) {
let noteId = { id: ele.dataset.id };
Modal.addDisplayModalHandler();
}
function init() {
/* NoteView.detectNoteChange(updateNoteDetails); */
NoteView.addHandlerShowModalOnClick(showModal);
NoteView.addHandlerRemoveCard(removeNote);
/* Modal.addDisplayModalHandler(displayModal); */
Modal.addHandlerCloseModal(closeModal);
}
init();
/Task.js/
import { callCorrectFunc } from "../helper.js";
import { Note as noteData } from "../model.js";
class NoteView {
_parentElement = document.querySelector(".task_card_container");
/* constructor(title, body, date, priority) {
this.title = title;
this.body = body;
this.date = date;
this.priority = priority;
} */
addHandlerRemoveCard(handler) {
document.querySelector(".notes_list").addEventListener("click", (e) => {
let ele = e.target.closest(".task_card") || null;
if (ele !== null) {
let action = getDataAttr(ele);
if (action === "open_modal") {
handler(ele);
}
}
});
}
/* addHandler(handler){
document.querySelector('.task_card_container').addEventListener('click', (e)=>{
callCorrectFunc(e);
})
} */
addHandlerShowModalOnClick(handler) {
document.querySelector(".notes_list").addEventListener("click", (e) => {
let ele = e.target.closest(".task_card") || null;
if (ele !== null) {
let action = getDataAttr(ele);
if (action === "open_modal") {
handler(ele);
}
}
});
}
detectNoteChange(handler) {
document
.querySelector(".task_card_container")
.addEventListener("keyup", (e) => {
console.log(e.target);
let ele = e.target.closest("div");
if (!ele) return;
handler(ele);
});
}
renderUI() {
let badgeColor =
noteData.currentNote.priority === "1"
? "text-bg-danger"
: noteData.currentNote.priority === "2"
? "text-bg-warning"
: "text-bg-success";
let template = this.#generateNoteTemplate(noteData.currentNote, badgeColor);
this._parentElement.insertAdjacentHTML("beforeend", template);
this.clearTaskModal();
}
removeNotefromView(note) {
document.querySelector(`div[data-id="${note.objectId}"]`).remove();
}
#generateNoteTemplate({ id, title, body, priority, objectId }, badgeColor) {
return `
<div class="task_card" data-id=${objectId} data-action="open_modal">
<div class="task_card_header">
<p id="task_card_header_text" contenteditable="true" >${title}</p>
<span class="material-symbols-outlined" id="delete_card_button" data-id=${objectId} data-action="close" role="button">disabled_by_default</span>
</div>
<div class="task_card_body">
<div class="task_card_body_content" contenteditable="true">
${body}
</div>
</div>
<div class="card_metadata">
<p class="card_metadata_body ${badgeColor}">
<span class="material-symbols-outlined">
priority_high
</span>
<span class="card_metadata_priority">${
priority === "1"
? "High"
: priority === "2"
? "Medium"
: "Low"
}</span>
</p>
<p class="card_metadata_body text-bg-secondary">
<span class="material-symbols-outlined">
schedule
</span>
<span class="card_metadata_date"> ${new Date().toLocaleDateString()}</span>
</p>
</div>
</div>`;
}
clearTaskModal() {
task_title.value = "";
task_body.value = "";
task_priority.value = "3";
}
}
export default new NoteView();
Index.HTML
<div class="task_card_container">
<div class="notes_list">
</div>
</div>
Upvotes: 0
Views: 79
Reputation: 13432
You do mention event-delegation, but it seems that both functions addHandlerShowModalOnClick
and addHandlerRemoveCard
do event-delegation not entirely correct. At least addHandlerRemoveCard
needs to be implemented in a way that it identifies the click-target as a remove-button and not alike addHandlerShowModalOnClick
where one generically targets ...
e.target.closest(".task_card");
For addHandlerRemoveCard
it rather should be something like ...
e.target.closest(".remove_button");
Identifying the remove-button click correctly is crucial for addHandlerRemoveCard
and every other action that is going to follow.
E.g. something like that ...
addHandlerRemoveCard(handler) {
document
.querySelector(".notes_list")
.addEventListener("click", ({ target }) => {
// - targeting a remove-button first, assuring
// that the note actually has to be removed.
//
// let ele = evt.target.closest(".task_card") || null;
const elmRemove = target.closest(".remove_button");
const elmCard = elmRemove && target.closest(".task_card");
if (elmCard !== null) {
handler(elmCard);
}
});
}
async function removeNote(ele) {
let noteId = { id: ele.dataset.id };
let deletedNote = await sendAPIRequest("deleteNote", "DELETE", noteId);
let localDeletedNote = deleteNoteFromLocal(deletedNote);
NoteView.removeNotefromView(localDeletedNote);
}
NoteView.addHandlerRemoveCard(removeNote);
Upvotes: 0
Reputation: 178375
Pass the event to your handlers
handler(ele,e);
and use stopPropagation
async function removeNote(ele, e) {
e.stopPropagation();
// ... rest of your code
}
function showModal(ele, e) {
e.stopPropagation();
// ... rest of your code
}
I personally prefer one event handler on the closest static container so something like this, then you do not need to pass the event and do stop propagastion in the function
addHandlerNoteActions(showModalHandler, removeNoteHandler) {
this._parentElement.addEventListener("click", (e) => {
const ele = e.target.closest(".task_card");
if (!ele) return;
const action = ele.dataset.action;
switch (action) {
case "open_modal":
e.stopPropagation();
showModalHandler(ele);
break;
case "close":
e.stopPropagation();
removeNoteHandler(ele);
break;
// ... other cases ...
}
});
}
Lastly I do not see the need for async operations other than for the removeNote
Upvotes: 1