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>
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