无论怎么修改这个函数,都无法通过3的判定。
所以到底要怎么做?
You use JSON.stringify in setBookmarks. That means that you set a string in local storage, right?
So wouldn’t that mean that you are also getting a string when you get from local storage?
然后一个新的问题,关于5号测试。
‘ 当 localStorage 中的 bookmarks 密钥不包含有效的书签对象数组时,getBookmarks 函数应返回一个空数组。’
那么我的想法是,1从localStorage获取的bookmarksArr是数组,2每个数组都是对象,3每个成员至少有必要的三个属性。
按照设想,改了几次都不能通过测试。
这一部分用来手动测试,都是预期的结果。

难不成对属性值也有要求?
但我看这个示例项目,也随便填就行呐。只要不是空格就行。
感谢您解决第一个问题。现在还有第二个问题需要解答。
It is much easier to help you if you post your code, not a screenshot. And a link to the challenge.
You are basing your attempts on the tests, please look also at the user stories, there is always more details.
But anyway, there can be an invalid value in local storage, and JSON.parse throws an error when used. Can your code deal with that?
Can your code deal with objects that do not have the required properties?
Build a Bookmark Manager App: 构建书签管理器应用 | freeCodeCamp.org
//主页
const mainSection = document.querySelector('#main-section');
const categoryDropdown = document.querySelector('#category-dropdown');
const viewCategoryButton = document.querySelector('#view-category-button');
const addBookmarkButton = document.querySelector('#add-bookmark-button');
//填写表单
const formSection = document.querySelector('#form-section');
const formH2 = document.getElementsByTagName('h2')[0];
const nameInput = document.querySelector('#name');
const urlInput = document.querySelector('#url');
const formBackButton = document.querySelector('#close-form-button');
const addBookmarkFormButton = document.querySelector('#add-bookmark-button-form');
//列表内容
const bookmarkListSection = document.querySelector('#bookmark-list-section');
const listH2 = document.getElementsByTagName('h2')[1];;
const categoryList = document.querySelector('#category-list');
const listBackButton = document.querySelector('#close-list-button');
const deleteBookmarkButton = document.querySelector('#delete-bookmark-button');
let bookmarksArr;
/*
bookmarksArr = [{name: '41', category: '2', url: 123}];
setBookmarks();
getBookmarks();//[ { name: '41', category: '2', url: 123 } ]
*/
function getBookmarks() {
bookmarksArr = JSON.parse(localStorage.getItem('bookmarks'));
if (!Array.isArray(bookmarksArr)) {
bookmarksArr = [];
} else {
const resu = bookmarksArr.every(obj => typeof obj === 'object');
if (resu) {
const result = bookmarksArr.some(obj => obj['name'] === undefined || obj['category'] === undefined || obj['url'] === undefined);
if (result) {
bookmarksArr = [];
}
} else {
bookmarksArr = [];
}
}
console.log(bookmarksArr);
return bookmarksArr;
}
function setBookmarks() {
localStorage.setItem('bookmarks',JSON.stringify(bookmarksArr));
}
function displayOrCloseForm() {
mainSection.classList.toggle('hidden');
formSection.classList.toggle('hidden');
}
function displayOrHideCategory() {
mainSection.classList.toggle('hidden');
bookmarkListSection.classList.toggle('hidden');
}
function reset() {
nameInput.value = '';
urlInput.value = '';
}
function getText() {
let text = categoryDropdown.value;
text = text.replace(text[0], text[0].toUpperCase());
return text;
}
function updateUI() {
const text = getText();
let resuArr = [];
bookmarksArr.forEach(item => {
if (item.category === text) {
resuArr.push(item);
}
});
//console.log('resuArr',resuArr);
if (resuArr.length === 0) {
categoryList.style.color = 'black';
categoryList.textContent = 'No Bookmarks Found';
} else {
let html;
resuArr.forEach(item => {
html += `<div><input type="radio" id="${item.name}"/><a href="${item.url}" target="_blank">${item.name}</a></div>`;
});
categoryList.innerHTML = html;
reset()
}
}
//主页按钮
viewCategoryButton.addEventListener('click', () => {
displayOrHideCategory();
const text = getText();
listH2.textContent = text;
getBookmarks();
updateUI();
});
addBookmarkButton.addEventListener('click', () => {
displayOrCloseForm();
const text = getText();
formH2.textContent = text;
reset();
});
//列表内容按钮
listBackButton.addEventListener('click', () => {
displayOrHideCategory();
});
deleteBookmarkButton.addEventListener('click', () => {
const checkedArr = document.querySelectorAll('div>input');
console.log('checkedArr', checkedArr);
checkedArr.forEach(item => {
for (let i = 0; i < bookmarksArr.length; i++) {
if (bookmarksArr[i].name === item.id && item.checked === true) {
bookmarksArr.splice(i, 1);
}
}
});
updateUI()
setBookmarks();
});
//表单按钮
formBackButton.addEventListener('click', () => {
displayOrCloseForm();
});
addBookmarkFormButton.addEventListener('click', () => {
if (nameInput.value.trim().length === 0 || urlInput.value.trim().length === 0) {
alert('Please, provide valid name and URL.');
reset()
} else {
bookmarksArr.push({name: nameInput.value, category: formH2.textContent, url: urlInput.value});
displayOrCloseForm();
setBookmarks()
}
});
1.请暂时忽略88行(//主页按钮)及以下的代码。因为getBookmarks函数没有通过5号测试,所以我没有更改下面的代码。
2.关于getBookmarks函数的测试,我通过手动测试,向localStorage写入了东西来测试。22行至26行(即let bookmarksArr;下面的几行代码)被注释的代码就是用来测试的。
向localStorage写入了以’bookmarks’为键的字符串/空数组/不包含对象的数组/缺少特定属性的对象数组,都显示没有问题,输出结果符合预期。
实在是想不到问题出在哪里。
<!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>
: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;
}
what if localStorage.getItem('bookmarks') retrieves a value like "hello"?
JSON.parse("hello") will throw an error. Can your code deal with an error from JSON.parse?
感谢您的提醒,通过增加try/catch代码,问题解决了。
产生这个问题的原因,是我设想的是,写入localStorage只通过localStorage.setItem(‘bookmarks’,JSON.stringify(bookmarksArr)),所以localStorage.getItem(‘bookmarks’)的值已经是JSON格式。
如果直接通过localStorage.setItem(‘bookmarks’,bookmarksArr),那么写入的数据会被直接转化为字符串。那就没有意义了。

It is true that your app does that, but sometimes local storage can be updated from something else, your app need to be able to handle corrupted local storage data
先生您好,这个项目又发现2个新的问题。
这是最新的代码。
//主页
const mainSection = document.querySelector('#main-section');
const categoryDropdown = document.querySelector('#category-dropdown');
const viewCategoryButton = document.querySelector('#view-category-button');
const addBookmarkButton = document.querySelector('#add-bookmark-button');
//填写表单
const formSection = document.querySelector('#form-section');
const formH2 = document.getElementsByTagName('h2')[0];
const nameInput = document.querySelector('#name');
const urlInput = document.querySelector('#url');
const formBackButton = document.querySelector('#close-form-button');
const addBookmarkButtonForm = document.querySelector('#add-bookmark-button-form');
//列表内容
const bookmarkListSection = document.querySelector('#bookmark-list-section');
const listH2 = document.getElementsByTagName('h2')[1];;
const categoryList = document.querySelector('#category-list');
const listBackButton = document.querySelector('#close-list-button');
const deleteBookmarkButton = document.querySelector('#delete-bookmark-button');
/*
bookmarksArr = [{name: '41', category: '2', url: 123}];
setBookmarks();
getBookmarks();//[ { name: '41', category: '2', url: 123 } ]
*/
function getBookmarks() {
let bookmarksArr;
try {
bookmarksArr = JSON.parse(localStorage.getItem('bookmarks'));
} catch(e) {
bookmarksArr = [];
}
if (!Array.isArray(bookmarksArr)) {
bookmarksArr = [];
} else {
const resu = bookmarksArr.every(obj => typeof obj === 'object');
if (resu) {
const result = bookmarksArr.some(obj => obj['name'] === undefined || obj['category'] === undefined || obj['url'] === undefined);
if (result) {
bookmarksArr = [];
}
} else {
bookmarksArr = [];
}
}
//console.log('bookmarksArr', bookmarksArr);
return bookmarksArr;
}
function setBookmarks(bookmarksArr) {
localStorage.setItem('bookmarks',JSON.stringify(bookmarksArr));
}
function displayOrCloseForm() {
mainSection.classList.toggle('hidden');
formSection.classList.toggle('hidden');
}
function displayOrHideCategory() {
mainSection.classList.toggle('hidden');
bookmarkListSection.classList.toggle('hidden');
}
function reset() {
nameInput.value = '';
urlInput.value = '';
}
function getText() {
let text = categoryDropdown.value;
text = text.replace(text[0], text[0].toUpperCase());
return text;
}
function updateUI(bookmarksArr) {
const text = getText().toLowerCase();
let resuArr = [];
bookmarksArr.forEach(item => {
if (item.category === text) {
resuArr.push(item);
}
});
//console.log('resuArr',resuArr);
if (resuArr.length === 0) {
categoryList.style.color = 'black';
categoryList.innerHTML = '<p>No Bookmarks Found</p>';
} else {
let html = '';
resuArr.forEach(item => {
html += `<div><input type="radio" id="${item.name}" name="bookmarks" value="${item.name}"/><label for="${item.name}"><a href="${item.url}" target="_blank">${item.name}</a></label></div>`;
});
categoryList.innerHTML = html;
reset();
}
}
//主页按钮
viewCategoryButton.addEventListener('click', () => {
displayOrHideCategory();
const text = getText();
listH2.textContent = text;
const bookmarksArr = getBookmarks();
updateUI(bookmarksArr);
});
addBookmarkButton.addEventListener('click', () => {
displayOrCloseForm();
const text = getText();
formH2.textContent = text;
reset();
});
//列表内容按钮
listBackButton.addEventListener('click', () => {
displayOrHideCategory();
});
deleteBookmarkButton.addEventListener('click', () => {
let bookmarksArr = getBookmarks();
const checkedArr = document.querySelectorAll('div>input');
//console.log('checkedArr', checkedArr);
//console.log(bookmarksArr);
checkedArr.forEach(item => {
for (let i = 0; i < bookmarksArr.length; i++) {
if (bookmarksArr[i].name === item.id && item.checked === true) {
bookmarksArr.splice(i, 1);
}
}
});
//console.log(bookmarksArr);
updateUI(bookmarksArr);
setBookmarks(bookmarksArr);
});
//表单按钮
formBackButton.addEventListener('click', () => {
displayOrCloseForm();
});
addBookmarkButtonForm.addEventListener('click', () => {
if (nameInput.value.trim().length === 0 || urlInput.value.trim().length === 0) {
alert('Please, provide valid name and URL.');
reset();
displayOrCloseForm();
} else {
let bookmarksArr = getBookmarks();
bookmarksArr.push({name: nameInput.value, category: getText().toLowerCase(), url: urlInput.value});
setBookmarks(bookmarksArr);
reset();
displayOrCloseForm();
}
});
//localStorage.clear();
1.如图,下面两个测试(13和15)始终无法通过。我确认上面两个函数没有问题
2.除了末尾的23号测试,其他测试都能通过。
所以为什么无法通过测试?
As you never shared it, here the link to the challenge: https://www.freecodecamp.org/learn/javascript-v9/lab-bookmark-manager-app/build-a-bookmark-manager-app
It is much easier to help with the link to the challenge, so please share it when you ask for help.
I test your code and I do not see tests 13 and 15 failing. Tests 13 and 15 pass with your code. Have you maybe changed the HTML?
For test 23, consider that there can be bookmarks with the same name in different categories.
1.关于13号和15号测试,我没有修改HTML代码,提供的JavaScript代码是可以通过这两个测试的。
请注意上次给您的回复中,里面的第一张图,注释掉了displayOrCloseForm()。这时就无法通过13号和15号测试。
2.再次说明下问题:
在示例项目的填写表单页面,没有任何填写并点击addBookmarkButtonForm按钮,只会显示alert弹窗,不会切换回主页面。
而通过13号和15号测试的代码,则是在表单页面没有任何填写的情况下,点击addBookmarkButtonForm按钮,会显示alert弹窗 + 切换回主页面。
关于23号测试,感谢您的提醒。现在代码已经通过所有测试 ![]()