Build a Bookmark Manager App - Build a Bookmark Manager App

Tell us what’s happening:

Hello there!

Regarding this lab: https://www.freecodecamp.org/learn/full-stack-developer/lab-bookmark-manager-app/build-a-bookmark-manager-app
I have some issues with the tests 18, 19, 20, 22 & 23 regarding the way I update categoryList.innerHTML and the way I delete the elements.
But when I test it, everything seems to just work fine and so does the browser’s localStorage.
Is there something wrong with what I wrote or do I miss something ?

Your code so far

/* file: script.js */
//Références vers les éléments du DOM
const mainSection = document.getElementById("main-section");
const formSection = document.getElementById("form-section");
const bookmarkListSection = document.getElementById("bookmark-list-section");

const addBookmarkBtn = document.getElementById("add-bookmark-button");
const categoryName = document.querySelectorAll(".category-name");
const category = document.getElementById("category-dropdown");

const closeFormBtn = document.getElementById("close-form-button");

const addBookmarkFormBtn = document.getElementById("add-bookmark-button-form");
const nameInput = document.getElementById("name");
const urlInput = document.getElementById("url");

const viewCategoryBtn = document.getElementById("view-category-button");
const categoryList = document.getElementById("category-list");

const closeListBtn = document.getElementById("close-list-button");
const deleteBookmarkBtn = document.getElementById("delete-bookmark-button");

// Récupération du tableau depuis le localStorage ou initialisation d'un tableau vide
const getBookmarks = () => {
  try {
    if (!localStorage.getItem("bookmarks")) {
      return []; // Si la clé "bookmarks" n'existe pas, la f° retourne un tab vide
    }  
    const bookmarks = JSON.parse(localStorage.getItem("bookmarks"));
    const isValidArray = Array.isArray(bookmarks) && bookmarks.every(isValidBookmark); // Test pour s'assurer que bookmarks soit bien un Array et test de la validité de chaque objet de bookmarks
    return isValidArray ? bookmarks : []; // Si tout est OK, la f° retourne bookmarks. Sinon elle retourne un tab vide.
  } catch(error) {
    return [];
  }
}

//Pour tous les éléments du tab en localStorage, la f° vérifie si l'élément est un objet existant et qu'il contienne bien les 3 clés : name, category, url afin de s'assurer de la validité de l'élément comme bookmark
const isValidBookmark = bookmark => {
  return bookmark !== null && typeof bookmark === "object" 
          && "name" in bookmark && "category" in bookmark && "url" in bookmark;
}

const displayOrCloseForm = () => {
  mainSection.classList.toggle("hidden");
  formSection.classList.toggle("hidden");
}

const resetForm = () => {
  nameInput.value = "";
  urlInput.value = "";
}

const displayOrHideCategory = () => {
  mainSection.classList.toggle("hidden");
  bookmarkListSection.classList.toggle("hidden");
}

const displayBookmarkList = () => {
  categoryList.innerHTML = "";
  const bookmarkList = getBookmarks();
  const matchingList = bookmarkList.filter(bookmark => bookmark.category === category.value);
  if (!matchingList.length) {
    categoryList.innerHTML = '<p>No Bookmarks Found</p>';
  } else {
    matchingList.forEach(({name, category, url}) => {
      categoryList.innerHTML += `
        <label for="${name}">
          <input type="radio" id="${name}" name="link-btn" value="${name}" />
          <a href="${url}">${name}</a>
        </label>`;
    });
  }
}

//List of Event Listeners

addBookmarkBtn.addEventListener("click", () => {
  categoryName[0].innerText = category.value;
  displayOrCloseForm(); 
});

closeFormBtn.addEventListener("click", () => {
  displayOrCloseForm(); 
});

addBookmarkFormBtn.addEventListener("click", () => {
  const bookmarkList = getBookmarks();
  const bookmarkObj = {
    name: nameInput.value,
    category: category.value,
    url: urlInput.value
  }; 

  if(!nameInput.value.trim() || !urlInput.value.trim()){
    alert("Please, provide valid name and URL.");
    resetForm();
    return;
  }

  bookmarkList.push(bookmarkObj); // Ajout de l'objet en fin de tableau
  localStorage.setItem("bookmarks", JSON.stringify(bookmarkList)); //MAJ du bookmarks dans le localStorage avec le nouvel objet
  resetForm();
  displayOrCloseForm();
});

viewCategoryBtn.addEventListener("click", () => {
  categoryName[1].innerText = category.value;
  displayBookmarkList();
  displayOrHideCategory(); 
});  

closeListBtn.addEventListener("click", () => {
  displayOrHideCategory();
});

deleteBookmarkBtn.addEventListener("click", () => {
  const checkedBookmark = document.querySelector('input[name="link-btn"]:checked');
  if (!checkedBookmark) return;
  
  const bookmarkList = getBookmarks();
  const bookmarkArrIndex = bookmarkList.findIndex(
    (bookmark) => bookmark.name === checkedBookmark.id
  );
  if (bookmarkArrIndex !== -1) {
    bookmarkList.splice(bookmarkArrIndex, 1);
    //checkedBookmark.parentElement.remove();
    localStorage.setItem("bookmarks", JSON.stringify(bookmarkList));
    displayBookmarkList();
  } 
});
<!-- file: index.html -->
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=0.0">
    <title>Bookmark Manager</title>
    <link rel="stylesheet" href="styles.css">
</head>

<body>
    <main>
        <section id="main-section">
            <h1>Bookmark Manager</h1>
            <div id="dropdown">
                <label for="category-dropdown">Select a category:</label>
                <select id="category-dropdown" name="options">
                    <option value="News" selected>News</option>
                    <option value="Entertainment">Entertainment</option>
                    <option value="Work">Work</option>
                    <option value="Miscellaneous">Miscellaneous</option>
                </select>
            </div>
            <div id="buttons">
                <button type="button" id="view-category-button">View Category</button>
                <button type="button" id="add-bookmark-button">Add Bookmark</button>
            </div>
        </section>

        <section id="form-section" class="hidden">
            <form>
                <h2 class="category-name"></h2>
                <div>
                    <label for="name">Name:</label>
                    <input type="text" id="name">
                </div>
                <div>
                    <label for="url">URL:</label>
                    <input type="text" id="url">
                </div>
                <div>
                    <button type="button" id="close-form-button">Go Back</button>
                    <button type="button" id="add-bookmark-button-form">Add Bookmark</button>
                </div>
            </form>
        </section>

        <section id="bookmark-list-section" class="hidden">
            <h2 class="category-name"></h2>
            <div id="category-list">
            </div>
            <div>
                <button type="button" id="close-list-button">Go Back</button>
                <button type="button" id="delete-bookmark-button">Delete Bookmark</button>
            </div>
        </section>

    </main>
    <script src="script.js"></script>
</body>

</html>
/* file: styles.css */
:root {
  --light-grey: #f5f6f7;
  --dark-grey: #0a0a23;
  --yellow: #f1be32;
  --golden-yellow: #feac32;
}

*,
*::before,
*::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

main {
  display: flex;
  justify-content: center;
}

body {
  background-color: var(--dark-grey);
}

.hidden {
  display: none;
}

section {
  display: flex;
  flex-direction: column;
  justify-content: center;
}

select,
input,
label {
  margin-left: 10px;
}

div {
  padding: 30px;
  display: flex;
  justify-content: center;
}

.close-form-button {
  background: none;
  border: none;
  cursor: pointer;
}

h1, h2 {
  margin-top: 20px;
  text-align: center;
}

#category-list {
  text-align: center;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  background-color: var(--light-grey);
  align-self: center;
  width: 80%;
  margin-top: 15px;
  border-radius: 10px;
}

#category-list,
h1,
h2,
label {
  color: var(--light-grey);
}

#category-list p {
  color: var(--dark-grey);
}

button {
  cursor: pointer;
  padding: 5px;
  width: 100px;
  margin: 10px;
  color: var(--dark-grey);
  background-color: var(--golden-yellow);
  background-image: linear-gradient(#fecc4c, #ffac33);
  border-color: var(--golden-yellow);
  border-width: 3px;
}

button:hover {
  background-image: linear-gradient(#ffcc4c, #f89808);
}

section {
  margin-top: 60px;
  border: 2px solid var(--golden-yellow);
  width: fit-content;
  border-radius: 10px;
}

Your browser information:

User Agent is: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36

Challenge Information:

Build a Bookmark Manager App - Build a Bookmark Manager App

you need to deal with

when the item in the local storage is not valid JSON

see the errors you get in the browser console

for deleting bookmark, can your app deal with bookmarks in different categories having the same name?

When it is not a valid JSON, it will throw an error and return an empty array. Each object of the array is tested individually within the isValidBookmark() function.

For the deleting part, I tried and tested multiple times with different cases including the one with same name, same url but different categories and only the one selected is deleted which is the expected behavior. So Im a bit confused right now…

these value attributes have been changed, changing the HTML was not part of the instructions

sorry for the previous useless comments, I think I was half asleep

Thanks. I changed back the HTML.

And I made a verification test on the bookmark.category in the delete function in order to solve test 23:

That made it pass all the tests.

Problem solved!