当数组没有重复时如何防止 .createElement 视图重复

how to prevent .createElement view duplication when array doesn't have duplication

let recipes = [];
let favouriteRecipes;
let ul = document.querySelector(".list-group");


const fetchRecipes = async function() {
  try {
    const res = await axios.get('https://api.spoonacular.com/recipes/complexSearch?apiKey=e7221d9e49e04040afd50e7b626e2f88&number=2');
    recipes.push(...res.data.results);
    createNewRecipe(recipes);
  } catch (error) {
    console.log(error);
  }
}

const createNewRecipe = function(recipes) {
  for (let recipe of recipes) {
    let li = document.createElement('li');
    li.textContent = recipe.title;
    li.id = recipe.id;
    li.className = "list-group-item";

    let img = document.createElement('img');
    img.src = recipe.image;
    img.className = "card-img-top";

    li.append(img);
    ul.append(li);

    let recipeId = document.getElementById(recipe.id).id;
    fetchIndividualRecipe(recipeId, li);
    addToFavourites(recipeId);
  }
}

const fetchIndividualRecipe = async function(recipeId, li) {
  try {
    let id = recipeId;
    const res = await axios.get(`https://api.spoonacular.com/recipes/${id}/information?apiKey=e7221d9e49e04040afd50e7b626e2f88&includeNutrition=true`)
    createRecipeDetails(res, li);
  } catch (error) {
    console.log(error);
  }
}

const createRecipeDetails = function(individualRecipeData, li) {
  let cookingTime = individualRecipeData.data.readyInMinutes;
  let calories;
  for (let nutrient of individualRecipeData.data.nutrition.nutrients) {
    if (nutrient.name === "Calories") {
      calories = nutrient.amount;
    }
  }
  let spanMinutes = document.createElement("span");
  let spanCalories = document.createElement("span");
  spanMinutes.className = "badge bg-secondary";
  spanCalories.className = "badge bg-secondary"
  spanMinutes.textContent = cookingTime + " minutes";
  spanCalories.textContent = calories + " calories";
  li.append(spanMinutes);
  li.append(spanCalories);
}

// Add to favourites
const addToFavourites = function(id) {
  document.getElementById(id).addEventListener('click', function() {
    let clickedId = parseInt(id);
    for (let recipe of recipes) {
      if (recipe.id === clickedId) {
        newFavRecipe = recipe;
        displayFavouriteRecipes(newFavRecipe)
      }
    }
  })
}

// Display favourites
const displayFavouriteRecipes = function(recipeReceived) {
  let newRecipe = recipeReceived;
  favouriteRecipes = JSON.parse(localStorage.getItem("favRecipes") || "[]");

  let foundRecipe = false;

  if (newRecipe) {
    for (let rec of favouriteRecipes) {
      if (rec.id.toString() === newRecipe.id.toString()) {
        foundRecipe = true;
      }
    }
    if (!foundRecipe) {
      favouriteRecipes.push(newRecipe);
    }
  }

  for (let favRec of favouriteRecipes) {
    if (foundRecipe !== true) {
      let h4 = document.createElement('h4');
      h4.innerHTML = favRec.title;
      document.body.append(h4);
    }
  }
  localStorage.setItem("favRecipes", JSON.stringify(favouriteRecipes));
}

fetchRecipes();
displayFavouriteRecipes();
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Recipes Project V1</title>
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous" />
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.5.0/font/bootstrap-icons.css" />
  <link rel="stylesheet" href="recipes-coding-project-v1.css" />
</head>

<body>
  <nav class="navbar navbar-expand-lg navbar-dark">
    <div class="container container-fluid">
      <a class="navbar-brand" href="#">
        <i class="bi bi-egg-fried"></i> Recipes
      </a>
      <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarText" aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation">
          <span class="navbar-toggler-icon"></span>
        </button>
      <div class="collapse navbar-collapse" id="navbarText">
        <ul class="navbar-nav me-auto mb-2 mb-lg-0">
          <li class="nav-item">
            <a class="nav-link" href="#">Recipe List</a>
          </li>
        </ul>
        <span class="navbar-text">
            Favourite Recipes
            <i class="bi bi-heart-fill empty-heart"></i>
          </span>
      </div>
    </div>
  </nav>

  <main class="container">
    <h1>Recipes</h1>
    <ul class="list-group list-group-flush"></ul>

    <h2>Favourite Recipes</h2>
    <ul class="list-group list-group-flush favourite-recipes-ul"></ul>
  </main>
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.21.1/axios.min.js"></script>
  <script src="recipes-coding-project-v1.js"></script>
</body>

</html>

目标:我只想在单击图片时添加一次 obj(食谱),将其保存到 localStorage 中的一个数组,并在视图中为我的 localStorage 数组中的每个食谱创建一个 h4。

问题:我的问题是,当我第一次创建新食谱并将其显示在视图中时,它仅在视图中复制,但在我的本地存储阵列中没有复制(按预期工作)。 但是,当我刷新时,视图会准确反映我的数组,而不是副本。没有错误或任何错误,我更改了我的代码几次,但我造成的伤害更大,它重复了每个食谱。

我的Html:

<main class="container">
      <h1>Recipes</h1>
      <ul class="list-group list-group-flush"></ul>

      <h2>Favourite Recipes</h2>
      <ul class="list-group list-group-flush favourite-recipes-ul"></ul>
</main>

这是我的代码:

// Add to favourites
const addToFavourites = function (id) {
    document.getElementById(id).addEventListener('click', function() {
       let clickedId = parseInt(id);
       for (let recipe of recipes) { 
           if(recipe.id === clickedId) {
            newFavRecipe = recipe;
            displayFavouriteRecipes(newFavRecipe)
        }
      }
    })
}

// Display favourites
const displayFavouriteRecipes = function (recipeReceived) {
    let newRecipe = recipeReceived;
    favouriteRecipes = JSON.parse(localStorage.getItem("favRecipes") || "[]");
    
let foundRecipe = false;

if (newRecipe) {
    for (let rec of favouriteRecipes) {
        if (rec.id.toString() === newRecipe.id.toString()) {
            foundRecipe = true;
        }
    }
    if (!foundRecipe) {
        favouriteRecipes.push(newRecipe);
    }
}

for (let favRec of favouriteRecipes) {
    if (foundRecipe !== true) {
        let h4 = document.createElement('h4');
        h4.innerHTML = favRec.title;
        document.body.append(h4);
    }
}
localStorage.setItem("favRecipes", JSON.stringify(favouriteRecipes));
}

fetchRecipes();
displayFavouriteRecipes();

我重写了很多代码。我分离了从 API 获取数据并创建 Element.

的逻辑

现在应该可以了。

const createNewRecipe = (recipes, indivRecipes) => {
  let ul = document.querySelector(".list-group");
  for (let i = 0; i < recipes.length; i++) {
    let li = document.createElement("li");
    li.textContent = recipes[i].title;
    li.id = recipes[i].id;
    li.setAttribute("title", recipes[i].title);
    li.className = "list-group-item";

    let img = document.createElement("img");
    img.src = recipes[i].image;
    img.className = "card-img-top";

    li.append(img);
    ul.append(li);
    createRecipeDetails(indivRecipes[i], li);
  }
};

const createRecipeDetails = function (individualRecipeData, li) {
  let cookingTime = individualRecipeData.data.readyInMinutes;
  let calories;
  for (let nutrient of individualRecipeData.data.nutrition.nutrients) {
    if (nutrient.name === "Calories") {
      calories = nutrient.amount;
    }
  }
  let spanMinutes = document.createElement("span");
  let spanCalories = document.createElement("span");
  spanMinutes.className = "badge bg-secondary";
  spanCalories.className = "badge bg-secondary";
  spanMinutes.textContent = cookingTime + " minutes";
  spanCalories.textContent = calories + " calories";
  li.append(spanMinutes);
  li.append(spanCalories);
};

//Click Event
document
  .querySelector("ul.list-group.list-group-flush")
  .addEventListener("click", addFavoriteRecipes);

function addFavoriteRecipes(event) {
  const li = event.target.closest("li");
  if (li) {
    const recipeId = li.id;
    const favoriteRecipes = JSON.parse(
      localStorage.getItem("favRecipes") || "[]"
    );

    const checkIndex = favoriteRecipes.findIndex(el => el.id === recipeId);
    if (checkIndex === -1) {
      const ul = document.querySelector("ul.favourite-recipes-ul");
      const h4 = document.createElement("h4");
      const title = li.getAttribute("title");
      h4.innerHTML = title;
      ul.append(h4);
      favoriteRecipes.push({ id: li.id, title: title });
      localStorage.setItem("favRecipes", JSON.stringify(favoriteRecipes));
    }
  }
}

const fetchIndivRecipe = async function (recipeId) {
  try {
    const res = await axios.get(
      `https://api.spoonacular.com/recipes/${recipeId}/information?apiKey=e7221d9e49e04040afd50e7b626e2f88&includeNutrition=true`
    );
    return res;
  } catch (error) {
    console.log(error);
    throw new Error(error);
  }
};

const fetchRecipes = async function () {
  try {
    const res = await axios.get(
      "https://api.spoonacular.com/recipes/complexSearch?apiKey=e7221d9e49e04040afd50e7b626e2f88&number=2"
    );
    // recipes.push(...res.data.results);
    // console.log({ recipes });
    return res.data.results;
  } catch (error) {
    console.log(error);
  }
};

const fetchAllIndivRecipes = async recipes => {
  const res = recipes.map(recipe => fetchIndivRecipe(recipe.id));
  return Promise.all(res);
};

const loadFavorite = () => {
  const favoriteRecipes = JSON.parse(
    localStorage.getItem("favRecipes") || "[]"
  );

  const ul = document.querySelector("ul.favourite-recipes-ul");
  for (let favRec of favoriteRecipes) {
    const h4 = document.createElement("h4");
    h4.innerHTML = favRec.title;
    ul.append(h4);
  }
};

const main = async () => {
  try {
    const recipes = await fetchRecipes();
    const indivRecipes = await fetchAllIndivRecipes(recipes);
    createNewRecipe(recipes, indivRecipes);
    loadFavorite();
  } catch (err) {
    console.log(err);
  }
};

main();
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Recipes Project V1</title>
    <link
      href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css"
      rel="stylesheet"
      integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC"
      crossorigin="anonymous"
    />
    <link
      rel="stylesheet"
      href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.5.0/font/bootstrap-icons.css"
    />
    <link rel="stylesheet" href="recipes-coding-project-v1.css" />
  </head>

  <body>
    <nav class="navbar navbar-expand-lg navbar-dark">
      <div class="container container-fluid">
        <a class="navbar-brand" href="#">
          <i class="bi bi-egg-fried"></i> Recipes
        </a>
        <button
          class="navbar-toggler"
          type="button"
          data-bs-toggle="collapse"
          data-bs-target="#navbarText"
          aria-controls="navbarText"
          aria-expanded="false"
          aria-label="Toggle navigation"
        >
          <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse" id="navbarText">
          <ul class="navbar-nav me-auto mb-2 mb-lg-0">
            <li class="nav-item">
              <a class="nav-link" href="#">Recipe List</a>
            </li>
          </ul>
          <span class="navbar-text">
            Favourite Recipes
            <i class="bi bi-heart-fill empty-heart"></i>
          </span>
        </div>
      </div>
    </nav>

    <main class="container">
      <h1>Recipes</h1>
      <ul class="list-group list-group-flush"></ul>

      <h2>Favourite Recipes</h2>
      <ul class="list-group list-group-flush favourite-recipes-ul"></ul>
    </main>
    <script
      src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
      integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM"
      crossorigin="anonymous"
    ></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.21.1/axios.min.js"></script>
    <script src="recipes-coding-project-v1.js"></script>
  </body>
</html>