I am working on a Notes Website and I seem to be having issues with my JavaScript function. I call it in my php code and it keeps talking about an Uncaught Reference Error and I have been unable to find a solution.
These are my codes below.
notes.php
<?php
// Initialize the session
session_start();
// Check if the user is logged in
if (!isset($_SESSION["loggedin"]) || $_SESSION["loggedin"] !== true) {
header("location: login.php");
exit;
}
// Include config file
require_once "config.php";
// Fetch notes for the current user
$user_id = $_SESSION["id"];
$sql = "SELECT * FROM notes WHERE user_id = ?";
if ($stmt = $mysqli->prepare($sql)) {
$stmt->bind_param("i", $user_id);
if ($stmt->execute()) {
$result = $stmt->get_result();
echo '<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>Notes | Noted App</title>
<link rel="stylesheet" href="notes.css">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Iconscout Link For Icons -->
<link rel="stylesheet" href="https://unicons.iconscout.com/release/v4.0.8/css/line.css">
</head>
<body>
<ul class="nav-links">
<li class="center"><a href="#">Dashboard</a></li>
<li class="upward"><a href="#">About</a></li>
<li class="forward"><a href="logout.php">Logout</a></li>
</ul>
<div class="popup-box">
<div class="popup">
<div class="content">
<header>
<p></p>
<i class="uil uil-times"></i>
</header>
<form action="#">
<div class="row title">
<label for="title">Title</label>
<input type="text" spellcheck="false" id="title">
</div>
<div class="row description">
<label for="desc">Description</label>
<textarea spellcheck="false" id="desc"></textarea>
</div>
<button></button>
</form>
</div>
</div>
</div>
<div class="wrapper">
<li class="add-box">
<div class="icon"><i class="uil uil-plus"></i></div>
<p>Add new note</p>
</li>
<div class="notes-list">'; // Start the notes list
while ($row = $result->fetch_assoc()) {
// Format the date
$formattedDate = date("F j, Y", strtotime($row['date_published']));
echo '<li class="note">
<div class="details">
<p>' . $row['title'] . '</p>
<span>' . nl2br($row['description']) . '</span>
</div>
<div class="bottom-content">
<span>' . $formattedDate . '</span>
<div class="settings">
<i onclick="showMenu(this)" class="uil uil-ellipsis-h"></i>
<ul class="menu">
<li onclick="updateNote(' . $row['id'] . ', \'' . $row['title'] . '\', \'' . addslashes($row['description']) . '\')"><i class="uil uil-pen"></i>Edit</li>
<li onclick="deleteNote(' . $row['id'] . ')"><i class="uil uil-trash"></i>Delete</li>
</ul>
</div>
</div>
</li>';
}
echo '</div></div><script src="script.js"></script></body></html>';
} else {
echo "Error fetching notes.";
}
$stmt->close();
}
$mysqli->close();
?>
script.js
document.addEventListener("DOMContentLoaded", function () {
const months = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
];
const addBox = document.querySelector(".add-box");
const popupBox = document.querySelector(".popup-box");
const popupTitle = popupBox.querySelector("header p");
const closeIcon = popupBox.querySelector("header i");
const titleTag = popupBox.querySelector("input");
const descTag = popupBox.querySelector("textarea");
const addBtn = popupBox.querySelector("button");
const notesList = document.querySelector(".notes-list");
addBox.addEventListener("click", () => {
popupTitle.innerText = "Add a new Note";
addBtn.innerText = "Add Note";
popupBox.classList.add("show");
document.querySelector("body").style.overflow = "hidden";
if (window.innerWidth > 660) titleTag.focus();
});
closeIcon.addEventListener("click", () => {
titleTag.value = descTag.value = "";
popupBox.classList.remove("show");
document.querySelector("body").style.overflow = "auto";
});
function showNotes() {
// Send an AJAX request to fetch notes
fetch("fetch_notes.php")
.then((response) => {
if (!response.ok) {
throw new Error("Network response was not ok");
}
return response.json();
})
.then((data) => {
if (data && data.length > 0) {
notesList.innerHTML = ""; // Clear existing notes
data.forEach((note) => {
let filterDesc = note.description.replaceAll("\n", "<br/>");
let liTag = `
<li class="note">
<div class="details">
<h3>${note.title}</h3>
<p>${filterDesc}</p>
<p>Date Published: ${note.date}</p>
</div>
<div class="bottom-content">
<div class="settings">
<i onclick="showMenu(this)" class="uil uil-ellipsis-h"></i>
<ul class="menu">
<li onclick="updateNote(${note.id}, '${note.title}', '${filterDesc}')"><i class="uil uil-pen"></i>Edit</li>
<li onclick="deleteNote(${note.id})"><i class="uil uil-trash"></i>Delete</li>
</ul>
</div>
</div>
</li>
`;
notesList.insertAdjacentHTML("beforeend", liTag);
});
} else {
// Handle case where there are no notes or the data is empty
notesList.innerHTML = "<p>No notes available.</p>";
}
})
.catch((error) => {
console.error("Error fetching notes:", error);
// Handle other errors, e.g., network issues or server errors
notesList.innerHTML = "<p>Error fetching notes. Please try again later.</p>";
});
}
showNotes();
// Initialize the openMenu variable
let openMenu = null;
// Define the showMenu function
function showMenu(triggeringElement, menu) {
// If another menu is open, close it
if (openMenu !== null && openMenu !== menu) {
openMenu.classList.remove("show");
}
// Toggle the menu
if (menu.classList.contains('show')) {
menu.classList.remove('show');
openMenu = null;
} else {
menu.classList.add('show');
openMenu = menu;
}
// Close the menu when clicking outside of it
document.addEventListener('click', function closeMenu(event) {
if (!menu.contains(event.target)) {
menu.classList.remove('show');
document.removeEventListener('click', closeMenu);
openMenu = null;
}
});
}
// Attach the showMenu function to your settings icons
document.addEventListener("DOMContentLoaded", function () {
const notesList = document.getElementById("notesList");
notesList.addEventListener("click", function (event) {
const settingsIcon = event.target.closest(".settings i");
if (settingsIcon) {
const menu = settingsIcon.nextElementSibling;
showMenu(settingsIcon, menu);
}
});
});
function deleteNote(noteId) {
let confirmDel = confirm("Are you sure you want to delete this note?");
if (!confirmDel) return;
// Send a DELETE request to delete the note
fetch("delete_note.php", {
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ id: noteId }),
})
.then((response) => response.json())
.then((data) => {
if (data.success) {
showNotes(); // Refresh the notes list
} else {
console.error("Error deleting note:", data.error);
}
})
.catch((error) => {
console.error("Error deleting note:", error);
});
}
function updateNote(noteId, title, filterDesc) {
let description = filterDesc.replaceAll("<br/>", "\r\n");
addBox.click();
titleTag.value = title;
descTag.value = description;
popupTitle.innerText = "Update a Note";
addBtn.innerText = "Update Note";
// Store the note ID to use when updating
addBtn.dataset.noteId = noteId;
}
addBtn.addEventListener("click", (e) => {
e.preventDefault();
let title = titleTag.value.trim();
let description = descTag.value.trim();
if (title || description) {
let currentDate = new Date();
let month = months[currentDate.getMonth()]; // Change 'month' to 'months'
let day = currentDate.getDate();
let year = currentDate.getFullYear();
let noteInfo = { title, description, date: `${month} ${day}, ${year}` };
let isUpdate = !!addBtn.dataset.noteId;
let url = isUpdate ? "update_note.php" : "add_note.php";
if (isUpdate) {
noteInfo.id = addBtn.dataset.noteId;
}
// Send an AJAX request to add/update the note
fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(noteInfo),
})
.then((response) => response.json())
.then((data) => {
if (data.success) {
showNotes(); // Refresh the notes list
closeIcon.click(); // Close the popup
} else {
console.error("Error saving note:", data.error);
}
})
.catch((error) => {
console.error("Error saving note:", error);
});
}
});
});
Something is also intefering with the card display of my notes. The title, description and date are not being displayed the way they were styled in my css. I have added my css code just in case anyone can help with that.
notes.css
/* Import Google Font - Poppins */
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap');
*{
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Poppins', sans-serif;
}
body{
background: #88ABFF;
}
::selection{
color: #fff;
background: #618cf8;
}
.wrapper{
margin: 50px;
display: grid;
gap: 25px;
grid-template-columns: repeat(auto-fill, 265px);
}
.wrapper li{
height: 250px;
list-style: none;
border-radius: 5px;
padding: 15px 20px 20px;
background: #fff;
box-shadow: 0 4px 8px rgba(0,0,0,0.05);
}
.add-box, .icon, .bottom-content,
.popup, header, .settings .menu li{
display: flex;
align-items: center;
justify-content: space-between;
}
.add-box{
cursor: pointer;
flex-direction: column;
justify-content: center;
}
.add-box .icon{
height: 78px;
width: 78px;
color: #88ABFF;
font-size: 40px;
border-radius: 50%;
justify-content: center;
border: 2px dashed #88ABFF;
}
.add-box p{
color: #88ABFF;
font-weight: 500;
margin-top: 20px;
}
.note{
display: flex;
flex-direction: column;
justify-content: space-between;
}
.note .details{
max-height: 165px;
overflow-y: auto;
}
.note .details::-webkit-scrollbar,
.popup textarea::-webkit-scrollbar{
width: 0;
}
.note .details:hover::-webkit-scrollbar,
.popup textarea:hover::-webkit-scrollbar{
width: 5px;
}
.note .details:hover::-webkit-scrollbar-track,
.popup textarea:hover::-webkit-scrollbar-track{
background: #f1f1f1;
border-radius: 25px;
}
.note .details:hover::-webkit-scrollbar-thumb,
.popup textarea:hover::-webkit-scrollbar-thumb{
background: #e6e6e6;
border-radius: 25px;
}
.note p{
font-size: 22px;
font-weight: 500;
}
.note span{
display: block;
color: #575757;
font-size: 16px;
margin-top: 5px;
}
.note .bottom-content{
padding-top: 10px;
border-top: 1px solid #ccc;
}
.bottom-content span{
color: #6D6D6D;
font-size: 14px;
}
.bottom-content .settings{
position: relative;
}
.bottom-content .settings i{
color: #6D6D6D;
cursor: pointer;
font-size: 15px;
}
.settings .menu{
z-index: 1;
bottom: 0;
right: -5px;
padding: 5px 0;
background: #fff;
position: absolute;
border-radius: 4px;
transform: scale(0);
transform-origin: bottom right;
box-shadow: 0 0 6px rgba(0,0,0,0.15);
transition: transform 0.2s ease;
}
.settings.show .menu{
transform: scale(1);
}
.settings .menu li{
height: 25px;
font-size: 16px;
margin-bottom: 2px;
padding: 17px 15px;
cursor: pointer;
box-shadow: none;
border-radius: 0;
justify-content: flex-start;
}
.menu li:last-child{
margin-bottom: 0;
}
.menu li:hover{
background: #f5f5f5;
}
.menu li i{
padding-right: 8px;
}
.popup-box{
position: fixed;
top: 0;
left: 0;
z-index: 2;
height: 100%;
width: 100%;
background: rgba(0,0,0,0.4);
}
.popup-box .popup{
position: absolute;
top: 50%;
left: 50%;
z-index: 3;
width: 100%;
max-width: 400px;
justify-content: center;
transform: translate(-50%, -50%) scale(0.95);
}
.popup-box, .popup{
opacity: 0;
pointer-events: none;
transition: all 0.25s ease;
}
.popup-box.show, .popup-box.show .popup{
opacity: 1;
pointer-events: auto;
}
.popup-box.show .popup{
transform: translate(-50%, -50%) scale(1);
}
.popup .content{
border-radius: 5px;
background: #fff;
width: calc(100% - 15px);
box-shadow: 0 0 15px rgba(0,0,0,0.1);
}
.content header{
padding: 15px 25px;
border-bottom: 1px solid #ccc;
}
.content header p{
font-size: 20px;
font-weight: 500;
}
.content header i{
color: #8b8989;
cursor: pointer;
font-size: 23px;
}
.content form{
margin: 15px 25px 35px;
}
.content form .row{
margin-bottom: 20px;
}
form .row label{
font-size: 18px;
display: block;
margin-bottom: 6px;
}
form :where(input, textarea){
height: 50px;
width: 100%;
outline: none;
font-size: 17px;
padding: 0 15px;
border-radius: 4px;
border: 1px solid #999;
}
form :where(input, textarea):focus{
box-shadow: 0 2px 4px rgba(0,0,0,0.11);
}
form .row textarea{
height: 150px;
resize: none;
padding: 8px 15px;
}
form button{
width: 100%;
height: 50px;
color: #fff;
outline: none;
border: none;
cursor: pointer;
font-size: 17px;
border-radius: 4px;
background: #6A93F8;
}
@media (max-width: 660px){
.wrapper{
margin: 15px;
gap: 15px;
grid-template-columns: repeat(auto-fill, 100%);
}
.popup-box .popup{
max-width: calc(100% - 15px);
}
.bottom-content .settings i{
font-size: 17px;
}
}
.nav-links{
display: flex;
align-items: center;
background: #fff;
padding: 20px 15px;
border-radius: 12px;
box-shadow: 0 5px 10px rgba(0,0,0,0.2);
}
.nav-links li{
list-style: none;
margin: 0 12px;
}
.nav-links li a{
position: relative;
color: #333;
font-size: 20px;
font-weight: 500;
padding: 6px 0;
text-decoration: none;
}
.nav-links li a:before{
content: '';
position: absolute;
bottom: 0;
left: 0;
height: 3px;
width: 0%;
background: #34efdf;
border-radius: 12px;
transition: all 0.4s ease;
}
.nav-links li a:hover:before{
width: 100%;
}
.nav-links li.center a:before{
left: 50%;
transform: translateX(-50%);
}
.nav-links li.upward a:before{
width: 100%;
bottom: -5px;
opacity: 0;
}
.nav-links li.upward a:hover:before{
bottom: 0px;
opacity: 1;
}
.nav-links li.forward a:before{
width: 100%;
transform: scaleX(0);
transform-origin: right;
transition: transform 0.4s ease;
}
.nav-links li.forward a:hover:before{
transform: scaleX(1);
transform-origin: left;
}
.notes-list {
display: contents;
margin: 0;
padding: 0;
border: none;
}
I keep getting the Uncaught Reference Error when i click on the settings icon which is supposed to display the edit and delete buttons.
I have tried everything, but that error won't go away.
The right way to do that is to change how button click is handled. Don't use it like this:
Intead of this add some class and attach as
addEventListener("click"...And I saw that function
showMenuaccept as a first paramtriggeringElement, but that variable is never used.EXPLANATION:
The
DOMContentLoadedcan be written different that you used, but with same results.Function
showMenuin this example is insidereadyfunction that is called on page load. BecauseshowMenuis not root function, it is nested inside other function, we can't call her directly withoutwindowprefixFunction
showMenu_2can be used as you want in your codeEXAMPLE
Here is an example for adding a click event instead of onclick attribute