Build a Bookmark Manager App - Build a Bookmark Manager App

Tell us what’s happening:

I only have tests 5 and 23 left. Surprisingly, none of the other questions helped, despite having the same issues. Please help.

Your code so far

<!-- 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: script.js */
function getBookmarks() {
    const bookmarks = localStorage.getItem('bookmarks');
    if (!bookmarks) return [];
    
    try {
        const parsedBookmarks = JSON.parse(bookmarks);
        if (Array.isArray(parsedBookmarks)) {
            return parsedBookmarks;
        } else {
            return [];
        }
    } catch (e) {
        return [];
    }
}

function displayOrCloseForm() {
    const mainSection = document.getElementById('main-section');
    const formSection = document.getElementById('form-section');
    mainSection.classList.toggle('hidden');
    formSection.classList.toggle('hidden');
}

function displayOrHideCategory() {
    const mainSection = document.getElementById('main-section');
    const bookmarkListSection = document.getElementById('bookmark-list-section');
    mainSection.classList.toggle('hidden');
    bookmarkListSection.classList.toggle('hidden');
}

document.getElementById('add-bookmark-button').addEventListener('click', () => {
    const categoryName = document.querySelector('#category-dropdown option:checked').text;
    document.querySelector('.category-name').innerText = categoryName;
    displayOrCloseForm();
});

document.getElementById('close-form-button').addEventListener('click', displayOrCloseForm);

document.getElementById('add-bookmark-button-form').addEventListener('click', () => {
    const name = document.getElementById('name').value;
    const url = document.getElementById('url').value;
    const category = document.getElementById('category-dropdown').value;

    const bookmarks = getBookmarks();
    bookmarks.push({ name, url, category });
    localStorage.setItem('bookmarks', JSON.stringify(bookmarks));

    document.getElementById('name').value = '';
    document.getElementById('url').value = '';

    displayOrCloseForm();
});

document.getElementById('view-category-button').addEventListener('click', () => {
    const selectedCategory = document.getElementById('category-dropdown').value;
    document.querySelector('.category-name').innerText = selectedCategory;

    const bookmarks = getBookmarks();
    const categoryList = document.getElementById('category-list');
    categoryList.innerHTML = '';

    const filteredBookmarks = bookmarks.filter(b => b.category === selectedCategory);

    if (filteredBookmarks.length === 0) {
        categoryList.innerHTML = '<p>No Bookmarks Found</p>';
    } else {
        filteredBookmarks.forEach(bookmark => {
            const radioId = bookmark.name.replace(/\s+/g, '-');
            categoryList.innerHTML += `
                <div>
                    <input type="radio" id="${radioId}" name="bookmark" value="${bookmark.name}">
                    <label for="${radioId}">
                        <a href="${bookmark.url}" target="_blank">${bookmark.name}</a>
                    </label>
                </div>
            `;
        });
    }

    displayOrHideCategory();
});

document.getElementById('close-list-button').addEventListener('click', displayOrHideCategory);

document.getElementById('delete-bookmark-button').addEventListener('click', () => {
    const bookmarks = getBookmarks();
    const selectedBookmarkName = document.querySelector('input[name="bookmark"]:checked');

    if (selectedBookmarkName) {
        const updatedBookmarks = bookmarks.filter(b => b.name !== selectedBookmarkName.value);
        localStorage.setItem('bookmarks', JSON.stringify(updatedBookmarks));
        displayOrHideCategory();
    }
});

/* 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 (X11; CrOS x86_64 14541.0.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36

Challenge Information:

Build a Bookmark Manager App - Build a Bookmark Manager App

for test 5, test your app with these

localStorage.setItem("bookmarks", 'invalid');
const arrayFromInvalidValue = getBookmarks();
console.log("Expected empty array:");
console.log(arrayFromInvalidValue);

console.log('\ntry 2');
localStorage.setItem("bookmarks", JSON.stringify([{name: "example1", category: "news", notUrl: "example1.com"}]));
const arrayFromInvalidArray = getBookmarks();
console.log("Expected empty array:");
console.log(arrayFromInvalidArray);

for the deletion of the bookmark, don’t close the category once the bookmark is deleted, that is causing some issues with the tests

Ok, that solved test 5, but what about test 23?

Current Javascript:

function getBookmarks() {
    const bookmarks = localStorage.getItem('bookmarks');
    if (!bookmarks) return [];
    
    try {
        const parsedBookmarks = JSON.parse(bookmarks);
        if (Array.isArray(parsedBookmarks)) {
            return parsedBookmarks.filter(b => 
                typeof b.name === 'string' && 
                typeof b.url === 'string' && 
                typeof b.category === 'string'
            );
        } else {
            return [];
        }
    } catch (e) {
        return [];
    }
}

function displayOrCloseForm() {
    const mainSection = document.getElementById('main-section');
    const formSection = document.getElementById('form-section');
    mainSection.classList.toggle('hidden');
    formSection.classList.toggle('hidden');
}

function displayOrHideCategory() {
    const mainSection = document.getElementById('main-section');
    const bookmarkListSection = document.getElementById('bookmark-list-section');
    mainSection.classList.toggle('hidden');
    bookmarkListSection.classList.toggle('hidden');
}

document.getElementById('add-bookmark-button').addEventListener('click', () => {
    const categoryName = document.querySelector('#category-dropdown option:checked').text;
    document.querySelector('.category-name').innerText = categoryName;
    displayOrCloseForm();
});

document.getElementById('close-form-button').addEventListener('click', displayOrCloseForm);

document.getElementById('add-bookmark-button-form').addEventListener('click', () => {
    const name = document.getElementById('name').value;
    const url = document.getElementById('url').value;
    const category = document.getElementById('category-dropdown').value;

    const bookmarks = getBookmarks();
    bookmarks.push({ name, url, category });
    localStorage.setItem('bookmarks', JSON.stringify(bookmarks));

    document.getElementById('name').value = '';
    document.getElementById('url').value = '';

    displayOrCloseForm();
});

document.getElementById('view-category-button').addEventListener('click', () => {
    const selectedCategory = document.getElementById('category-dropdown').value;
    document.querySelector('.category-name').innerText = selectedCategory;

    const bookmarks = getBookmarks();
    const categoryList = document.getElementById('category-list');
    categoryList.innerHTML = '';
    const filteredBookmarks = bookmarks.filter(b => b.category === selectedCategory);

    if (filteredBookmarks.length === 0) {
        categoryList.innerHTML = '<p>No Bookmarks Found</p>';
    } else {
        filteredBookmarks.forEach(bookmark => {
            const radioId = bookmark.name.replace(/\s+/g, '-');
            categoryList.innerHTML += `
                <div>
                    <input type="radio" id="${radioId}" name="bookmark" value="${bookmark.name}">
                    <label for="${radioId}">
                        <a href="${bookmark.url}" target="_blank">${bookmark.name}</a>
                    </label>
                </div>
            `;
        });
    }

    displayOrHideCategory();
});

document.getElementById('close-list-button').addEventListener('click', displayOrHideCategory);

document.getElementById('delete-bookmark-button').addEventListener('click', () => {
    const bookmarks = getBookmarks();
    const selectedBookmarkName = document.querySelector('input[name="bookmark"]:checked');

    if (selectedBookmarkName) {
        const updatedBookmarks = bookmarks.filter(b => b.name !== selectedBookmarkName.value);
        localStorage.setItem('bookmarks', JSON.stringify(updatedBookmarks));
        displayOrHideCategory();
    }
});
const viewCategoryButtonTest = document.getElementById("view-category-button");
const deleteBookmarkButtonTest = document.getElementById("delete-bookmark-button");
const categoryDropdownTest = document.getElementById("category-dropdown");
localStorage.setItem("bookmarks", JSON.stringify([{name: "example1", category: "news", url: "example1.com"}, {name: "example2", category: "entertainment", url: "example2.com"}, {name: "example3", category: "work", url: "example3.com"}, {name: "example4", category: "news", url: "example4.com"}]));
  categoryDropdownTest.value = "news";  
  viewCategoryButtonTest.dispatchEvent(new Event("click"));
  const firstRadioButton = document.querySelector('#category-list input[type="radio"]');
  firstRadioButton.checked = true;
  deleteBookmarkButtonTest.dispatchEvent(new Event("click"));
  let bookmarksDisplayed = document.querySelectorAll('#category-list input[type="radio"]');
  let bookmarkLabelsDisplayed = document.querySelectorAll('#category-list label');
  console.log("actual:",bookmarksDisplayed.length, "expected:", 1);

there is one more bookmarksDisplayed than there should be, this is not the whole test for the bookmark deletion but it’s where it break

doing the things by the hand the bookmarks go away, but the requirement is to still be on that same category of bookmarks, I think the issue may be there (try to delete a bookmark in the sample app for comparison)

Okay, so I believe got the bug, but the test itself won’t pass.

Current Javascript:

function getBookmarks() {
    const bookmarks = localStorage.getItem('bookmarks');
    if (!bookmarks) return [];
    
    try {
        const parsedBookmarks = JSON.parse(bookmarks);
        if (Array.isArray(parsedBookmarks)) {
            return parsedBookmarks.filter(b => 
                typeof b.name === 'string' && 
                typeof b.url === 'string' && 
                typeof b.category === 'string'
            );
        } else {
            return [];
        }
    } catch (e) {
        return [];
    }
}

function displayOrCloseForm() {
    const mainSection = document.getElementById('main-section');
    const formSection = document.getElementById('form-section');
    mainSection.classList.toggle('hidden');
    formSection.classList.toggle('hidden');
}

function displayOrHideCategory() {
    const mainSection = document.getElementById('main-section');
    const bookmarkListSection = document.getElementById('bookmark-list-section');
    mainSection.classList.toggle('hidden');
    bookmarkListSection.classList.toggle('hidden');
}

document.getElementById('add-bookmark-button').addEventListener('click', () => {
    const categoryName = document.querySelector('#category-dropdown option:checked').text;
    document.querySelector('.category-name').innerText = categoryName;
    displayOrCloseForm();
});

document.getElementById('close-form-button').addEventListener('click', displayOrCloseForm);

document.getElementById('add-bookmark-button-form').addEventListener('click', () => {
    const name = document.getElementById('name').value;
    const url = document.getElementById('url').value;
    const category = document.getElementById('category-dropdown').value;

    const bookmarks = getBookmarks();
    bookmarks.push({ name, url, category });
    localStorage.setItem('bookmarks', JSON.stringify(bookmarks));

    document.getElementById('name').value = '';
    document.getElementById('url').value = '';

    displayOrCloseForm();
});

document.getElementById('view-category-button').addEventListener('click', () => {
    const selectedCategory = document.getElementById('category-dropdown').value;
    document.querySelector('.category-name').innerText = selectedCategory;

    const bookmarks = getBookmarks();
    const categoryList = document.getElementById('category-list');
    categoryList.innerHTML = '';
    const filteredBookmarks = bookmarks.filter(b => b.category === selectedCategory);

    if (filteredBookmarks.length === 0) {
        categoryList.innerHTML = '<p>No Bookmarks Found</p>';
    } else {
        filteredBookmarks.forEach(bookmark => {
            const radioId = bookmark.name.replace(/\s+/g, '-');
            categoryList.innerHTML += `
                <div>
                    <input type="radio" id="${radioId}" name="bookmark" value="${bookmark.name}">
                    <label for="${radioId}">
                        <a href="${bookmark.url}" target="_blank">${bookmark.name}</a>
                    </label>
                </div>
            `;
        });
    }

    displayOrHideCategory();
});

document.getElementById('close-list-button').addEventListener('click', displayOrHideCategory);

document.getElementById('delete-bookmark-button').addEventListener('click', () => {
    const bookmarks = getBookmarks();
    const selectedBookmarkName = document.querySelector('input[name="bookmark"]:checked');

    if (selectedBookmarkName) {
        const updatedBookmarks = bookmarks.filter(b => b.name !== selectedBookmarkName.value);
        localStorage.setItem('bookmarks', JSON.stringify(updatedBookmarks));

        const selectedCategory = document.getElementById('category-dropdown').value;
        const filteredBookmarks = updatedBookmarks.filter(b => b.category === selectedCategory);
        const categoryList = document.getElementById('category-list');
        categoryList.innerHTML = '';

        if (filteredBookmarks.length === 0) {
            categoryList.innerHTML = '<p>No Bookmarks Found</p>';
        } else {
            filteredBookmarks.forEach(bookmark => {
                const radioId = bookmark.name.replace(/\s+/g, '-');
                categoryList.innerHTML += `
                    <div>
                        <input type="radio" id="${radioId}" name="bookmark" value="${bookmark.name}">
                        <label for="${radioId}">
                            <a href="${bookmark.url}" target="_blank">${bookmark.name}</a>
                        </label>
                    </div>
                `;
            });
        }
    }
});

your last challenge is to set this

  localStorage.setItem("bookmarks", JSON.stringify([{name: "duplicated-name", category: "news", url: "example1.com"}, {name: "duplicated-name", category: "entertainment", url: "example2.com"}]))

delete one bookmark without deleting the other

I’ve been working on it for a while now, and I don’t think I have gotten anywhere. I’ve been questioning if I have the code in the correct spot.

document.getElementById('delete-bookmark-button').addEventListener('click', () => {
    const bookmarks = getBookmarks();
    const selectedBookmarkName = document.querySelector('input[name="bookmark"]:checked');

    if (selectedBookmarkName) {
        const updatedBookmarks = bookmarks.filter(b => b.name !== selectedBookmarkName.value);
        localStorage.setItem("bookmarks", JSON.stringify([
    {name: "duplicated-name", category: "news", url: "example1.com"},
    {name: "duplicated-name", category: "entertainment", url: "example2.com"}
]));
        const selectedCategory = document.getElementById('category-dropdown').value;
        const filteredBookmarks = updatedBookmarks.filter(b => b.category === selectedCategory);
        const categoryList = document.getElementById('category-list');
        categoryList.innerHTML = '';

        if (filteredBookmarks.length === 0) {
            categoryList.innerHTML = '<p>No Bookmarks Found</p>';
        } else {
            filteredBookmarks.forEach(bookmark => {
                const radioId = bookmark.name.replace(/\s+/g, '-');
                categoryList.innerHTML += `
                    <div>
                        <input type="radio" id="${radioId}" name="bookmark" value="${bookmark.name}">
                        <label for="${radioId}">
                            <a href="${bookmark.url}" target="_blank">${bookmark.name}</a>
                        </label>
                    </div>
                `;
            });
        }
    }
});

the middle one is where I put the code

you should put any code used for testing after your code, including this snipped:

  localStorage.setItem("bookmarks", JSON.stringify([{name: "duplicated-name", category: "news", url: "example1.com"}, {name: "duplicated-name", category: "entertainment", url: "example2.com"}]))

then to manually test, go to one of the two categories (News or Entertainment), delete the bookmark there, then go to the other category and verify if the other bookmark is still there

Okay, so my code works now, but the final test still doesn’t pass.

Current Javascript:

function getBookmarks() {
    const bookmarks = localStorage.getItem('bookmarks');
    if (!bookmarks) return [];
    
    try {
        const parsedBookmarks = JSON.parse(bookmarks);
        if (Array.isArray(parsedBookmarks)) {
            return parsedBookmarks.filter(b => 
                typeof b.name === 'string' && 
                typeof b.url === 'string' && 
                typeof b.category === 'string'
            );
        } else {
            return [];
        }
    } catch (e) {
        return [];
    }
}

function displayOrCloseForm() {
    const mainSection = document.getElementById('main-section');
    const formSection = document.getElementById('form-section');
    mainSection.classList.toggle('hidden');
    formSection.classList.toggle('hidden');
}

function displayOrHideCategory() {
    const mainSection = document.getElementById('main-section');
    const bookmarkListSection = document.getElementById('bookmark-list-section');
    mainSection.classList.toggle('hidden');
    bookmarkListSection.classList.toggle('hidden');
}

document.getElementById('add-bookmark-button').addEventListener('click', () => {
    const categoryName = document.querySelector('#category-dropdown option:checked').text;
    document.querySelector('.category-name').innerText = categoryName;
    displayOrCloseForm();
});

document.getElementById('close-form-button').addEventListener('click', displayOrCloseForm);

document.getElementById('add-bookmark-button-form').addEventListener('click', () => {
    const name = document.getElementById('name').value;
    const url = document.getElementById('url').value;
    const category = document.getElementById('category-dropdown').value;

    const bookmarks = getBookmarks();
    bookmarks.push({ name, url, category });
    localStorage.setItem('bookmarks', JSON.stringify(bookmarks));

    document.getElementById('name').value = '';
    document.getElementById('url').value = '';

    displayOrCloseForm();
});

document.getElementById('view-category-button').addEventListener('click', () => {
    const selectedCategory = document.getElementById('category-dropdown').value;
    document.querySelector('.category-name').innerText = selectedCategory;

    const bookmarks = getBookmarks();
    const categoryList = document.getElementById('category-list');
    categoryList.innerHTML = '';
    const filteredBookmarks = bookmarks.filter(b => b.category === selectedCategory);

    if (filteredBookmarks.length === 0) {
        categoryList.innerHTML = '<p>No Bookmarks Found</p>';
    } else {
        filteredBookmarks.forEach(bookmark => {
            const radioId = bookmark.name.replace(/\s+/g, '-');
            categoryList.innerHTML += `
                <div>
                    <input type="radio" id="${radioId}" name="bookmark" value="${bookmark.name}">
                    <label for="${radioId}">
                        <a href="${bookmark.url}" target="_blank">${bookmark.name}</a>
                    </label>
                </div>
            `;
        });
    }

    displayOrHideCategory();
});

document.getElementById('close-list-button').addEventListener('click', displayOrHideCategory);

document.getElementById('delete-bookmark-button').addEventListener('click', () => {
    const bookmarks = getBookmarks();
    const selectedBookmarkName = document.querySelector('input[name="bookmark"]:checked');

    if (selectedBookmarkName) {
        const updatedBookmarks = bookmarks.filter(b => b.name !== selectedBookmarkName.value);
        localStorage.setItem('bookmarks', JSON.stringify(updatedBookmarks));

        const selectedCategory = document.getElementById('category-dropdown').value;
        const filteredBookmarks = updatedBookmarks.filter(b => b.category === selectedCategory);
        const categoryList = document.getElementById('category-list');
        categoryList.innerHTML = '';

        if (filteredBookmarks.length === 0) {
            categoryList.innerHTML = '<p>No Bookmarks Found</p>';
        } else {
            filteredBookmarks.forEach(bookmark => {
                const radioId = bookmark.name.replace(/\s+/g, '-');
                categoryList.innerHTML += `
                    <div>
                        <input type="radio" id="${radioId}" name="bookmark" value="${bookmark.name}">
                        <label for="${radioId}">
                            <a href="${bookmark.url}" target="_blank">${bookmark.name}</a>
                        </label>
                    </div>
                `;
            });
        }
    }
});
localStorage.setItem("bookmarks", JSON.stringify([{name: "duplicated-name", category: "news", url: "example1.com"}, {name: "duplicated-name", category: "entertainment", url: "example2.com"}]))

I just had to add the snippet to the bottom to make the code work.

share your code please

are you sure it works? because if I go to News, delete the bookmark, and the go to Entertainment, there is no bookmark there either

you are still deleting both at once, it doesn’t work

That’s… strange. I deleted the bookmark in News, went to Entertainment, and it said no bookmarks were there. I thought it worked, but I’m assuming I was just seeing things.

I’ve been checking for bugs, but I don’t see any existing bugs in my code. I’ve also been working on the code, but nothing I do seems to work. It either has no effect or makes a lot of passes stop passing.

I finally passed it, and now my brain is exhausted. Thank you for your help, @ILM!