'How to scroll down page to item id from localstorage favorite list

I have a code for a localstorage favorite list. It's working fine for example if I click on Article 1 (id: 22) or Article 4 (id: 25) it is added to the favorite list. So far fine. But what I need and can't do alone is: after I add some article id to My Favorite id's (In local storage), then I need when I click on that favorited article id item , I need the page to scroll down to its content.

Javascript code:

//index db

const favoriteButtonAttr = 'data-add-to-favorite';
const isFavorite = 'data-is-favorite';
const listSelector = '[data-my-favorites]';

class FavoritesList {
  constructor () {
    this.storageName = 'favoritesList';
    this.list = this.initList();
  }
  
  initList () {
    if (window.localStorage.getItem(this.storageName)) { 
      // todo: repetitive parse?
      const list = JSON.parse(window.localStorage.getItem(this.storageName));
      updateHtmlList(list);
      return list;
    } else {   
      return [];
    }
  }
  
  initButton(button) {
    const id = parseInt(button.getAttribute(favoriteButtonAttr));
    
    button.addEventListener('click', (event) => {
      const button = event.target;
      !inArray(id, this.list) ? this.list.push(id) : removeFromArray(id, this.list);
      setState(id, this.list);
      this.updateList();
    })
    
    function setState (id, list) {
      return button.toggleAttribute(isFavorite, inArray(id, list));
    }
    
    setState(id, this.list);
    return button;
  }
  
  updateList() {
    setLocalStorage(this.storageName, this.list);
    updateHtmlList(this.list); 
  }
}

function updateHtmlList(list) {
  if(list.length > 0) {
    // lastest favorites on top & don't modify original list;
    const newList = list.slice(0).reverse();
    favoritesHTMLElement.innerHTML = '';
    listItems = document.createElement('ul');
    newList.forEach( item => {
      let htmlLi = document.createElement('li');
      htmlLi.innerHTML = item;
      favoritesHTMLElement.appendChild(htmlLi);
    });
  } else {
    favoritesHTMLElement.innerHTML = '';
  }
} 

function inArray(element, array) {
  return array.indexOf(element) != -1;
}

function removeFromArray(element, array) {
  array.splice(array.indexOf(element), 1); 
}

function setLocalStorage(key, value) {
  console.log(value)
  window.localStorage.setItem(key, JSON.stringify(value));
}

const buttons = document.querySelectorAll(`[${favoriteButtonAttr}]`);
const favoritesHTMLElement = document.querySelector(listSelector);
let favorites = new FavoritesList();  
buttons.forEach( button => favorites.initButton(button) );

Html code:

<article class="layout">
  <div class="shop">
    <h1>Our articles</h1>
    <p>Articles will be added to favorite list ordered by last added.</p>
    <ul>
      <li id="22">
        <span>Article 1 (id: 22)</span>
        <button class="shop-item__favorite-button" data-add-to-favorite="22" data-favorite-title="Meredith dress">
          <span class="sr-only"
                data-favorite="Add to my favorites"
                data-favorite-added="Remove from my favorites"></span>
        </button>
      </li>
      <li id="23">
        <span>Article 2 (id: 23)</span>
        <button class="shop-item__favorite-button" data-add-to-favorite="23" data-favorite-title="Meredith Skirt">
          <span class="sr-only"
                data-favorite="Add to my favorites"
                data-favorite-added="Remove from my favorites"></span>
        </button>
      </li>
      <li id="24">
        <span>Article 3 (id: 24)</span>
        <button class="shop-item__favorite-button" data-add-to-favorite="24" data-favorite-title="Josephine short">
          <span class="sr-only"
                data-favorite="Add to my favorites"
                data-favorite-added="Remove from my favorites"></span>
        </button>
      </li>
      <li id="25">
        <span>Article 4 (id: 25)</span>
        <button class="shop-item__favorite-button" data-add-to-favorite="25" data-favorite-title="Josephine shirt">
          <span class="sr-only"
                data-favorite="Add to my favorites"
                data-favorite-added="Remove from my favorites"></span>
        </button>
      </li>
    </ul>
  </div>

  <section class="favorites">
    <h2>My Favorite id's (In local storage)</h2>
    <ul class="favorites__list" data-my-favorites data-my-favorites-empty="No favorites yet"></ul>
  </section>
</article>


Solution 1:[1]

You have almost done. Check this:

function updateHtmlList(list) {
  if(list.length > 0) {
    // lastest favorites on top & don't modify original list;
    const newList = list.slice(0).reverse();
    favoritesHTMLElement.innerHTML = '';
    listItems = document.createElement('ul');
    newList.forEach( item => {
      let htmlLi = document.createElement('li');

      htmlLi.innerHTML = '<a href="#'+item+'" class="go-to-link">'+item+'</a>';      

      favoritesHTMLElement.appendChild(htmlLi);
    });
  } else {
    favoritesHTMLElement.innerHTML = '';
  }
} 

Instead adding only a text with the article id, you can add an anchor link. Your articles already have an identifier, so you only need a lin to that identifiers (with the # to indicate that you navigate to that point of the page). I added also a class go-to-link that you can use to give style to the link. But it's not mandatory.

UPDATE

Well, this change need a function to do the scroll. We can use this https://stackoverflow.com/a/43698126/18452174 with some changes because that function don't work when you do scroll from botton to top:

function scrollTo(animate, to, duration, extraOffset) {
  // When should we finish?
  var finishAt = Date.now() + duration;
  
  // Start
  requestAnimationFrame(tick);

  function tick() {
    // How many frames left? (60fps = 16.6ms per frame)
    var framesLeft = (finishAt - Date.now()) / 16.6;


  // How far do we have to go?
  var distance = to.getBoundingClientRect().top + (extraOffset || 0);
  if (distance == 0) {
    // Done (this shouldn't happen, belt & braces)
    return;
  }

  // Adjust by one frame's worth
  if (framesLeft <= 1) {
    // Last call
    animate.scrollTop += distance;
  } else {
    // Not the last, adjust and schedule next
    var dir = distance < 0 ? -1 : 1;
    animate.scrollTop += dir * Math.max(1, dir * distance / framesLeft);
    requestAnimationFrame(tick);
   }
  }
 }

I recomended you the use of jQuery (if you can, of course) because the previous function is a single animate call and jQuery manage cross browser. Your code become smaller and is a powerfull library.

Now, we need to change the foreach of updateHtmlList function:

  let htmlLi = document.createElement('li');
  
  let htmlA = document.createElement('a');
  htmlA.setAttribute('href', '#' + item);
  htmlA.setAttribute('class', 'go-to-link');
  htmlA.innerText = item;
  htmlA.addEventListener('click', function (e) {
    e.preventDefault();

     var link = document.getElementById(item)
     scrollTo(document.documentElement, link, 300, -100);
  });
  
  htmlLi.appendChild(htmlA);

  favoritesHTMLElement.appendChild(htmlLi);

Instead using a string for the link, we create the "a" element because we want to addEventListener. In the click event, we abort (preventDefault) the default action (scroll to anchor) and do the scroll with the animated function. You can change the time (300 milliseconds in the example) and also, we can add some offset here if we want. You said previously -100px, so you can add -100 to the destination scroll position.

Here https://jsfiddle.net/mn3zx4j0/1/ you have a version of the previous code with an extra to highlight the article. After add -100px, the scroll don't stop exactly in the article an maybe a bit confuse. So add an extra parameter (a callback function) to the scroll animation and when the scroll finish, the function is executed.

function highlightScrolled(item) {
  var color = item.style.backgroundColor;
    item.style.backgroundColor='#ffff00';

  setTimeout(function() {
    item.style.backgroundColor=color;
  }, 3000);
}

This function highligh the article with a yellow back color during 3 seconds. You can change other style if you prefer (bold, color, underline...)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1