Reputation:
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.
Upvotes: 1
Views: 277
Reputation: 1136
You can add this code:
window.showMenu = function(){
// put your code here
}
Upvotes: 0
Reputation: 1459
The right way to do that is to change how button click is handled. Don't use it like this:
<i onclick="showMenu(this)" ...
Intead of this add some class and attach as addEventListener("click"...
<i class="showMenu" class="uil uil-ellipsis-h"></i>
<script>
showMenu.addEventListener("click", () => {
showMenu();
});
</script>
And I saw that function showMenu
accept as a first param triggeringElement
, but that variable is never used.
EXPLANATION:
The DOMContentLoaded
can be written different that you used, but with same results.
Function showMenu
in this example is inside ready
function that is called on page load. Because showMenu
is not root function, it is nested inside other function, we can't call her directly without window
prefix
document.addEventListener("DOMContentLoaded", function () {
ready();
});
function ready() {
/* ... some code here */
function showMenu() {
console.log("showMenu function is called");
}
window.showMenu_2 = function () {
console.log("window function showMenu_2 is called");
};
/* some code here ... */
}
Function showMenu_2
can be used as you want in your code
EXAMPLE
Here is an example for adding a click event instead of onclick attribute
document.addEventListener("DOMContentLoaded", function () {
// Define variable
const show_menu = document.querySelector(".showMenu");
// Button click event
show_menu.addEventListener("click", () => {
showMenu();
});
// Define the showMenu function
function showMenu(triggeringElement, menu) {
console.log("showMenu");
}
});
/* 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;
}
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8" />
<title>Notes | Noted App</title>
<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>
<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">
<div class="notes-list">
<li class="note">
<div class="details">
<p>Some title</p>
<span>description</span>
</div>
<div class="bottom-content">
<span>12.12.2012</span>
<div class="settings">
<i class="showMenu uil uil-ellipsis-h"></i>
<ul class="menu">
<li onclick="updateNote(1)"><i class="uil uil-pen"></i>Edit</li>
<li onclick="deleteNote(1)"><i class="uil uil-trash"></i>Delete</li>
</ul>
</div>
</div>
</li>
</div>
</div>
</body>
</html>
Upvotes: 0