What is the best way to add a "Save to Favorites" link within a modal window

I have integrated the NASA API to showcase images, where clicking on an image reveals a modal containing additional details. There is also a save to favorites functionality in the tutorial that allows users to save images and details to a local storage section.

My query revolves around implementing the add to favorites feature link within the modals and subsequently saving the image and details to the favorites section in local storage. How can I achieve this?

const resultsNav = document.getElementById("resultsNav");
const favoritesNav = document.getElementById("favoritesNav");
const imagesContainer = document.querySelector(".images-container");
const saveConfirmed = document.querySelector(".save-confirmed");
const loader = document.querySelector(".loader");

// NASA API
const count = 3;
const apiKey = 'DEMO_KEY';
const apiUrl = `https://api.nasa.gov/planetary/apod?api_key=${apiKey}&count=${count}`;

let resultsArray = [];
let favorites = {};

// Show Content
function showContent(page) {
  window.scrollTo({
    top: 0,
    behavior: "instant"
  });
  if (page === "results") {
    resultsNav.classList.remove("hidden");
    favoritesNav.classList.add("hidden");
  } else {
    resultsNav.classList.add("hidden");
    favoritesNav.classList.remove("hidden");
  }
  loader.classList.add("hidden");
}

// Create DOM Nodes
function createDOMNodes(page) {
  const currentArray =
    page === "results" ? resultsArray : Object.values(favorites);
  currentArray.forEach((result) => {
    // Card Container
    const card = document.createElement("div");
    card.classList.add("card");

    // Link that wraps the image
    const link = document.createElement("a");

    // Get the modal
    var modal = document.getElementById("myModal");

    // Get the image and insert it inside the modal - use its "alt" text as a caption
    var img = document.getElementById("myImg");
    var modalImg = document.getElementById("img01");

    var captionText = document.getElementById("caption");
    img.onclick = function() {
      modal.style.display = "block";
      modalImg.src = event.target.src;
      captionText.innerHTML = event.target.alt;
    }

    // Get the <span> element that closes the modal
    var span = document.getElementsByClassName("close")[0];

    // When the user clicks on <span> (x), close the modal
    span.onclick = function() {
      modal.style.display = "none";
    }

    // Image
    const image = document.createElement("img");
    image.src = result.url;
    image.alt = result.title + "<br>" + result.explanation + "<br>" + ' ' + result.date;
    image.loading = "lazy";
    image.classList.add("card-img-top");

    // Card Body
    const cardBody = document.createElement("div");
    cardBody.classList.add("card-body");

    // Card Title
    const cardTitle = document.createElement("h5");
    cardTitle.classList.add("card-title");
    cardTitle.textContent = result.title;

    // Save Text
    const saveText = document.createElement("p");
    saveText.classList.add("clickable");
    if (page === "results") {
      saveText.textContent = "Add To Favorites";
      saveText.setAttribute("onclick", `saveFavorite('${result.url}')`);
    } else {
      saveText.textContent = "Remove Favorite";
      saveText.setAttribute("onclick", `removeFavorite('${result.url}')`);
    }

    // Card Text
    const cardText = document.createElement("p");
    cardText.textContent = result.explanation;

    // Footer Conatiner
    const footer = document.createElement("small");
    footer.classList.add("text-muted");

    // Date
    const date = document.createElement("strong");
    date.textContent = result.date;

    // Copyright
    const copyrightResult =
      result.copyright === undefined ? "" : result.copyright;
    const copyright = document.createElement("span");
    copyright.textContent = ` ${copyrightResult}`;

    // Append everything together
    footer.append(date, copyright);
    cardBody.append(cardTitle, saveText, cardText, footer);
    link.appendChild(image);
    card.append(link); 
    imagesContainer.appendChild(card);
  });
}

// Update the DOM
function updateDOM(page) {
  if (localStorage.getItem("nasaFavorites")) {
    favorites = JSON.parse(localStorage.getItem("nasaFavorites"));
  }
  imagesContainer.textContent = "";
  createDOMNodes(page);
  showContent(page);
}

// Get 10 images from NASA API
async function getNasaPictures() {
  loader.classList.remove("hidden");
  try {
    const response = await fetch(apiUrl);
    resultsArray = await response.json();
    updateDOM("results");
  } catch (error) {
    // Error Handling
  }
}

// Add result to favorites
function saveFavorite(itemUrl) {
  resultsArray.forEach((item) => {
    if (item.url.includes(itemUrl) && !favorites[itemUrl]) {
      favorites[itemUrl] = item;
      saveConfirmed.hidden = false;
      setTimeout(() => {
        saveConfirmed.hidden = true;
      }, 2000);
      localStorage.setItem("nasaFavorites", JSON.stringify(favorites));
    }
  });
}

// Remove item from favorites
function removeFavorite(itemUrl) {
  if (favorites[itemUrl]) {
    delete favorites[itemUrl];
    localStorage.setItem("nasaFavorites", JSON.stringify(favorites));
    updateDOM("favorites");
  }
}

getNasaPictures();

Answer №1

Intriguing query! Upon checking the Nasa.api, I discovered that the server will provide an array containing objects. Therefore, an optimal approach would be to utilize an array to store all the liked items for easier display.

My recommendation is to directly store the information given by Nasa.api into an array and save it in localStorage. This will facilitate the display process later on.

I have also formulated a sample using Nasa.api for your reference. Although I lack your complete code and methodology for obtaining/displaying it, you can observe how I modified the localStorage section.

Within the sample I created, each time a user clicks the like button, it will be saved to the liked array and stored in localStorage. Every other click will remove the item from the liked array and update localStorage.

let source;
let xhr = new XMLHttpRequest();
let link = "https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY&count=5"
xhr.open('GET', link, true);
xhr.send();
let liked = JSON.parse(localStorage.getItem("likeditem"));

if (liked == null) liked = [];
xhr.onreadystatechange = function() {
  if (xhr.readyState === 4) {
    source = JSON.parse(xhr.responseText);
    create()
  }
}
function create() {
  for (let i = 0; i < source.length; i++) {
    console.log(source[i])
    document.body.innerHTML += `
         <img src=${source[i].url}>
         <div >${source[i].title}</div>
         <div >Date: ${source[i].date}</div>
          <div>Copyright: ${source[i].copyright}</div>
          <div>Media_type: ${source[i].media_type}</div>
          <div>Service_version: ${source[i].service_version}</div>
         <div>Explanation: ${source[i].explanation}</div>
          <button onclick='save(${i})'>Save to like</button>
`
    window.save = function(index) {
      let thisbutton = document.querySelectorAll('button')[index]
      if (thisbutton.textContent == 'Save to like') {
        liked.push(source[index])
        localStorage.setItem('likeditem', JSON.stringify(liked));
        thisbutton.textContent = "Saved"
      } else {
        liked.splice(liked.indexOf(source[index]), 1)
        localStorage.setItem('likeditem', JSON.stringify(liked));
        thisbutton.textContent = 'Save to like'
      }
    }
  }
}

As for displaying elements from localStorage, here's an example:

Having already saved all image info in the localStorage, we can simply employ the same method used for displaying info from Nasa.api.

let liked = JSON.parse(localStorage.getItem("likeditem"));

for (let i in liked){
if(!liked) break;;
        document.body.innerHTML += `
         <img src=${liked[i].url}>
         <div >${liked[i].title}</div>
         <div >Date: ${liked[i].date}</div>
          <div>Copyright: ${liked[i].copyright}</div>
          <div>Media_type: ${liked[i].media_type}</div>
          <div>Service_version: ${liked[i].service_version}</div>
         <div>Explanation: ${liked[i].explanation}</div>
          `
}

Update:

Save to localStorage:

https://i.sstatic.net/zByFl.png

Display from localStorage:

https://i.sstatic.net/sLC5Z.jpg

Answer №2

UPDATE: I have made some edits to my previous response. The following code snippet should be useful to you. (Please note that this code will not function here, so please try it out on your own system as localStorage is not supported in this environment.)

let nasaImages = [
    {
        id:1,
        url:"https://example.com/image-url-1"
    },
    {
        id:2,
        url:"https://example.com/image-url-2"
    },
    {
        id:3,
        url:"https://example.com/image-url-3"
    }
];

let favs = [];

// Check if the key exists in localstorage
if(localStorage.nasaFavs){
    favs = JSON.parse(localStorage.nasaFavs)
}

renderImages();

function renderImages(){
    $("#images").html(nasaImages.map(image => (
            `<div>
                id: ${image.id}
                <img src='${image.url}'>
                ${favBtn(image.id)}
            </div>`
        )
    ).join(""));
}

// Render the favorite button
function favBtn(id){
    if(favs.includes(id)){
        return `<button onclick="favUnfav(false,${id})">Remove from fav</button>`;
    }
    else{
        return `<button onclick="favUnfav(true,${id})">Add to fav</button>`;
    }
}

function favUnfav(isFav=true, id=null){
    // Write your logic to add to fav or remove from fav
    if(isFav){
        favs.push(id);
    }
    else{
        favs = favs.filter(e => e!=id);
    }

    // Save in localstorage
    localStorage.nasaFavs = JSON.stringify(favs);

    // Re-render the DOM
    renderImages()
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="images"></div>

Answer №3

Like @ba2sik mentioned earlier, start by familiarizing yourself with the localStorage API.

Currently, you are storing the entire favorites object in local storage using the key nasaFavorites. To retrieve it, simply use

JSON.parse(localStorage.getItem('nasaFavorites'))
to parse the data whenever you need to access it.

Keep in mind that the code provided does not account for edge cases or errors, so it's advisable to handle them using try/catch.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

When JSF renders bootstrap buttons, they appear without any horizontal padding

There is a strange behavior that I cannot explain... I am working with a series of buttons: <h:commandButton value="foo" actionListener="#{foo.foo}" styleClass="btn btn-danger"> <f:ajax render=":form"></f:ajax> </h:co ...

I have discovered some amazing jQuery image hover effects that are simply breathtaking –

I have been using the Adipoli jQuery Image Hover Effects plugin, but I am facing issues with changing certain properties. The image is set to change to grayscale initially and then switch to color on hover. However, when I click on the image, it should mai ...

Arrange the navigation bar in a straight line

I am facing an issue with my navigation bar... I want it to be centered but it is not working. I want it to fill from left to right. I have tried using the following CSS: text-align: center in .navbar a text-align: center in navbar. .navbar { backgr ...

Implementing Angular to activate the JavaScript 'click' event

I am attempting to initiate an 'onclick' event within an angular controller. <div id="galerie"> <h2>{{main.Page.title()}}</h2> <hr> <div class="row"> <div class="col-sm-4 col-xs-6" ng-repeat= ...

Retrieving data from router in Angular

I have been working on a web application using Angular, and I have implemented a fixed header in the index.html file. The header appears on every page, with content injected through a ui-view. However, I now want to display this header only after the user ...

Guide on transforming Div content to json format with the use of jquery

I am trying to figure out how to call the div id "con" when the export button is clicked in my HTML code. I want it to display JSON data in the console. If anyone has any suggestions or solutions, please help! <html> <div id ="con"> < ...

Tips for accessing the value from an input field in an AngularJS application

Looking at my DOM structure below, <input class="k-textbox ng-pristine ng-untouched ng-valid ng-valid-required" type="text" placeholder="" ng-blur="controller.isDirty=true" ng-change="controller.isDirty=true" ng-model="controller.model.value" ng-requir ...

Adding double quotes, where they haven't been added yet

I am trying to work with the following string (Very strong=="Very strong"?100:(Very strong=="Above Average"?75:(Very strong=="Average"?50:(Very strong=="Below Average"?25:(Very strong=="Cannot determine"?0:(Very strong=="Poor"?0:0)))))) My desired outpu ...

What is the best way to retrieve an array that was created using the useEffect hook in React?

Utilizing the power of useEffect, I am fetching data from two different APIs to build an array. My goal is to access this array outside of useEffect and utilize it in the return statement below to render points on a map. However, when trying to access it ...

Navigating through pages: How can I retrieve the current page value for implementing next and previous functions in Angular 7?

Greetings, I am a new learner of Angular and currently working on custom pagination. However, I am facing difficulty in finding the current page for implementing the next and previous functions. Can anyone guide me on how to obtain the current page value? ...

Adding elements to an array in Node.js

In my 'both' object, I need to store 2 arrays: eng and fr. Each of these arrays contains multiple objects. How can I transfer all values from frDisplayData to fr:[] within the 'both' object: const displayData = []; var both = {eng:dis ...

Using jQuery to retrieve the TD value

I'm attempting to retrieve the TD value using the Value attribute.... Let's say I have the following HTML markup: <td nowrap="nowrap" value="FO2180TL" class="colPadding" id="salesOrderNumber1">bla bla </td> So, I tried this- v ...

Incorporate adjustable div heights for dynamically toggling classes on various DOM elements

One of the challenges in developing a chat widget is creating an editable div for users to input text. In order to maintain the design integrity, the box needs to be fixed to the bottom. An essential requirement is to have javascript detect resizing as us ...

Merge the JSON data with the Node.js/Express.js response

Whenever I input somedomain.com/some_api_url?_var1=1 in a browser, the response that I receive is {"1":"descriptive string"}. In this JSON response, the index 1 can vary from 1 to n, and the "descriptive string" summarizes what the index represents. I am ...

It's recommended to utilize the callback feature with setLoggedIn to ensure the previous state is captured effectively, as illustrated below:

To ensure capturing the previous state when using setLoggedIn, it is recommended to utilize the callback option. This will help in keeping track of changes and ensuring smoother functionality: ...

Ways to retrieve the initial key object received through an AJAX request

I have a form with three dynamic dropdowns. The second dropdown depends on the first one, and the third dropdown depends on the second one. Therefore, selecting an option in the first dropdown will automatically populate the next two dropdowns. Below is t ...

There seems to be a problem as exits.success is not a recognized function -

Currently, I am exploring how to utilize Sails.js with the node-machine-syntax specifically in the context of listing flights. The code snippet below outlines my attempt at achieving this: /** * FlightsController * * @description :: Server-side actions fo ...

Encountering an issue with React JS Array Filtering: running into the error message "

I am encountering an error stating that includes is not a function, and the record.id is not being recognized in VS Code. I'm not sure where the problem lies? import React, { Component } from 'react' import axios from "axios" export de ...

Set the Checkbox selections by utilizing the array values in the MUI DataGrid

In my MUI Datagrid, I have 2 columns for ID and City Name. There are only 3 cities represented in the table. Within a citizen object like this: const userMockup = { id: 1, name: "Citizen 1", cities: [1, 2], isAlive: true }; The cities ar ...

Deactivate the signup button automatically when the user is not present, without the need to refresh

To determine user availability, the system will check the table of users. If the user is available, the signup button will be displayed; otherwise, the button will remain disabled or its color may change. ...