Creating a favorite icon toggle button

Interesting challenge to create a simple heart icon that toggles to a filled heart icon. With each press it either calls a function to favorite or unfavorite an item in a news feed. It needs to send in an news item ID so that the correct item gets updated.

      <div id="heartoff">
          <a href="#" class="link">
            <i class="icon f7-icons ios-only">heart</i>
            <i class="material-icons md-only">favorite_border</i>
          </a>
      </div>
      <div hidden id="hearton">
          <a href="#" class="link">
            <i class="icon f7-icons ios-only">heart_fill</i>
            <i class="material-icons md-only">favorite</i>
          </a>
      </div>

My thought was that in my routine I could toggle the ‘hidden’ value of the div to show the empty or full heart icon … but then I realized I might have hundreds of these so how would I know which div to toggle without giving them all unique names (that sounds painful).

In addition I want to call my function to increase or decrease the favorite count. I imagine I could embed a snipped of JS to catch the onclick and make the call. Since this is in a template (componentUrl) I can insure the function gets called with {{this.newsitemID}}

If F7 has an easier way to manage a two state icon and its function calls please fill me in! (especially if its possible to do most of it in HTML without jumping out to the methods on this page)

Added an onclick event to the heartoff div:

      <div id="heartoff" onclick="addFavorite(this, 5)">

The function then sets the current element to display:none which works. For some reason accessing the nextSibling returns a #text value but getting the nextSibling.nextSibling returns the second div (hearton) which I then set to display:block … and this fails.

** how do I get the function to format as code in this forum? **

function addFavorite(elem, id) {
elem.style.display = “none”;
elem.nextSibling.nextSibling.display = “block”;
}

If it is a router component, just use template features to bind it to some value:

<a href="#" class="link">
  <i class="icon f7-icons ios-only">{{#if isFavorite}}heart_fill{{else}}heart{{/if}</i>
  <i class="material-icons md-only">{{#if isFavorite}}favorite{{else}}favorite_border{{/if}}</i>
</a>

This heart icon is part of a card list in a ComponentUrl file so I imagine I can set the initial value of the heart icon using your example. But this only sets the initial value of the heart icon. When the user clicks on the heart to either favorite or unfavorite a particular card that I need to shift the state of the heart from empty to filled (or the reverse) and set the database boolean.

Its so strange to me that my addFavorite function works for elem.style.display but fails for elem.nextSibling

Here is my html inside the ComponentUrl:

    <div class="card-footer">
      <div class="heartoff" onclick="addFavorite(this, 5)">
          <a href="#" class="link">
            <i class="icon f7-icons ios-only">heart</i>
            <i class="material-icons md-only">favorite_border</i>
          </a>
      </div>
      <div class="hearton" style="display:none;">
          <a href="#" class="link">
            <i class="icon f7-icons ios-only">heart_fill</i>
            <i class="material-icons md-only">favorite</i>
          </a>
      </div>
    </div>

Is there a reason why elem.nextSibling of heartOff div return a #text item rather than the heartOn div? Is F7 doing something behind the scenes with the DOM?

SOLUTION - simple favoriting using heart icons
(for anyone who is integrating Firestore with Framework7)

Firebase query in offers page:

<script>
  return {
    data() {
      return {
        offers: [],
      };
    },
    mounted() {
      const self = this;
      self.getItems();
    },
    methods: {
      async getItems() {
        const self = this;
        let offerList = [];
        let offersRef = firestore.collection('offers').orderBy('createdOn', 'desc').limit(25);
        let allOffersSnapShot = await offersRef.get();
        allOffersSnapShot.forEach(doc => {
            let data = doc.data();
            data.id = doc.id;
            offerList.push(data);
        });
        self.$setState({ offers: offerList })
      }
    }
  }
</script>

HTML on offers page:

      <div class="heartoff" onclick="addFavorite(this, '{{this.id}}')">
          <a href="#" class="link">
            <i class="icon f7-icons ios-only">heart</i>
            <i class="material-icons md-only">favorite_border</i>
          </a>
      </div>
      <div class="hearton" onclick="subFavorite(this, '{{this.id}}')" style="display:none;">
          <a href="#" class="link">
            <i class="icon f7-icons ios-only">heart_fill</i>
            <i class="material-icons md-only">favorite</i>
          </a>
      </div>

Increment/Decrement in app.js:

function addFavorite(elem, id) {
    if (isUserSignedIn) {
        elem.style.display = "none";
        elem.nextSibling.nextSibling.style.display = "block";
  
        const increment = firebase.firestore.FieldValue.increment(1);
        firestore.collection('offers').doc(id).update({ favorites: increment });

    }
}

function subFavorite(elem, id) {
    if (isUserSignedIn) {
        elem.style.display = "none";
        elem.previousSibling.previousSibling.style.display = "block";
  
        const decrement = firebase.firestore.FieldValue.increment(-1);
        firestore.collection('offers').doc(id).update({ favorites: decrement });

    }
}
1 Like