Help me recreate a JS react system

Hello, everyone!

I would like to change the react system of a script I buy online.
Currently, the script has a react system to posts like Facebook and I would like to make it much simpler.

The Schema is pretty simple:
Like a button in black when there’s no reaction transforms into a colored one when the user presses it and reacts and turns back to black when the user presses it again(the colored one) and unreact to the post.

I would live below all the lines from the JS file and the tpl file. Please help me to transform it.
Thanks in advance. :pray:

tpl

 <!-- post actions -->
        {if $user->_logged_in && $_get != "posts_information"}
        
          <div class="post-actions">
            <!-- reactions -->
            <div class="action-btn reactions-wrapper {if $post['i_react']}js_unreact-post{/if}" data-reaction="{$post['i_reaction']}">
              <!-- reaction-btn -->
              
              <div class="reaction-btn">
                {if !$post['i_react']}
                 <div class="reaction-btn-icon">
                  </div>
               
                {else}
               <div class="reaction-btn-icon">
                    <div class="inline-emoji no_animation">
                      {include file='__reaction_emojis.tpl' _reaction=$post['i_reaction']}
                    </div>
                  </div>
                {/if}
              </div>
              
              <!-- reaction-btn -->

                <div class="reaction-btn-icon">
                {foreach $reactions_enabled as $reaction}
                  <div class="reactions_item  reaction-{$reaction@iteration} js_react-post" data-reaction="{$reaction['reaction']}" data-reaction-color="{$reaction['color']}">
                    {include file='__reaction_emojis.tpl' _reaction=$reaction['reaction']}
                  </div>
                {/foreach}
              </div>
            </div>
            <!-- reactions -->

JS

 // handle reactions
  function _show_reactions(element) {
    var _this = $(element);
    var reactions = _this.find('.reactions-container');
    var offset = _this.offset();
    var reactions_height = ($(window).width() < 480) ? 144 : 48;
    var posY = (offset.top - $(window).scrollTop()) - reactions_height;
    var posX = offset.left - $(window).scrollLeft();
    if ($('html').attr('dir') == "RTL") {
      var right = $(window).width() - posX - _this.width();
      reactions.css({ 'top': posY + 'px', 'right': right + 'px' });
    } else {
      reactions.css({ 'top': posY + 'px', 'left': posX + 'px' });
    }
    reactions.show();
  }
  function _hide_reactions(element) {
    var _this = $(element);
    var reactions = _this.find('.reactions-container:first');
    reactions.removeAttr('style').hide();
  }
  /* reactions toggle */
  $('body').on('mouseenter', '.reactions-wrapper', function () {
    if (!is_iPad() && $(window).width() >= 970) {
      /* desktop -> show the reactions */
      _show_reactions(this);
    }
  });
  $('body').on('mouseleave', '.reactions-wrapper', function () {
    if (!is_iPad() && $(window).width() >= 970) {
      /* desktop -> hide the reactions */
      _hide_reactions(this);
    }
  });
  $('body').on('click', '.reactions-wrapper', function () {
    if (is_iPad() || $(window).width() < 970) {
      /* mobile -> toggle the reactions */
      if ($(this).find('.reactions-container:first').is(":visible")) {
        /* hide the reactions */
        _hide_reactions(this);
      } else {
        /* hide any previous reactions */
        $('.reactions-container').removeAttr('style').hide();
        /* show the reactions */
        _show_reactions(this);
      }
    } else {
      /* desktop -> unreact */
      var _this = $(this);
      var old_reaction = _this.data('reaction');
      if (old_reaction) {
        if (_this.hasClass('js_unreact-post')) {
          var _undo = 'unreact_post';
          var handle = 'post';
          var _parent = _this.closest('.post, .lightbox-post, .article');
        } else if (_this.hasClass('js_unreact-photo')) {
          var _undo = 'unreact_photo';
          var handle = 'photo';
          var _parent = _this.closest('.post, .lightbox-post');
        } else if (_this.hasClass('js_unreact-comment')) {
          var _undo = 'unreact_comment';
          var handle = 'comment';
          var _parent = _this.closest('.comment');
        }
        var id = _parent.data('id');
        var reactions_wrapper = _parent.find('.reactions-wrapper:first');
        /* remove unreact from reactions-wrapper */
        reactions_wrapper.removeClass('js_unreact-' + handle);
        /* remove reactions-wrapper data-reaction */
        reactions_wrapper.data('reaction', '');
        /* change reaction-btn-name */
        _parent.find('.reaction-btn-name:first').text(__['Minused 1']).removeAttr('style');
        /* change reaction-btn-icon */
        _parent.find('.reaction-btn-icon:first').html('<i class="far fa-smile fa-lg fa-fw"></i>');
        /* hide reactions-container */
        _parent.find('.reactions-container:visible').removeAttr('style').hide();
        /* AJAX */
        $.post(api['posts/reaction'], { 'do': _undo, 'reaction': old_reaction, 'id': id }, function (response) {
          /* check the response */
          if (response.callback) {
            eval(response.callback);
          }
        }, 'json')
          .fail(function () {
            modal('#modal-message', { title: __['Error'], message: __['There is something that went wrong!'] });
          });
      }
    }
  });
  /* close reactions when clicked outside */
  $('body').on('click', function (e) {
    if ($(e.target).hasClass('reactions-wrapper') || $(e.target).parents('.reactions-wrapper').length > 0) {
      return;
    }
    $('.reactions-container').removeAttr('style').hide();
  });
  /* close reactions when user scroll (vertical) */
  var prevTop = 0;
  $(window).scroll(function (e) {
    var currentTop = $(this).scrollTop();
    if (prevTop !== currentTop) {
      prevTop = currentTop;
      $('.reactions-container').removeAttr('style').hide();
    }
  });
  /* react post & photo & comment */
  $('body').on('click', '.js_react-post, .js_react-photo, .js_react-comment', function (e) {
    e.stopPropagation();
    var _this = $(this);
    var reaction = _this.data('reaction');
    var reaction_color = _this.data('reaction-color');
    var reaction_title = _this.data('title');
    var reaction_html = _this.html();
    if (_this.hasClass('js_react-post')) {
      var _do = 'react_post';
      var _undo = 'unreact_post';
      var handle = 'post';
      var _parent = _this.closest('.post, .lightbox-post, .article');
    } else if (_this.hasClass('js_react-photo')) {
      var _do = 'react_photo';
      var _undo = 'unreact_photo';
      var handle = 'photo';
      var _parent = _this.closest('.post, .lightbox-post');
    } else if (_this.hasClass('js_react-comment')) {
      var _do = 'react_comment';
      var _undo = 'unreact_comment';
      var handle = 'comment';
      var _parent = _this.closest('.comment');
    }
    var id = _parent.data('id');
    var reactions_wrapper = _parent.find('.reactions-wrapper:first');
    var old_reaction = reactions_wrapper.data('reaction');
    /* check if user react or unreact */
    if (reactions_wrapper.hasClass('js_unreact-' + handle) && old_reaction == reaction) {
      /* [1] user unreact */
      /* remove unreact class from reactions-wrapper */
      reactions_wrapper.removeClass('js_unreact-' + handle);
      /* remove reactions-wrapper data-reaction */
      reactions_wrapper.data('reaction', '');
      /* change reaction-btn-name */
      _parent.find('.reaction-btn-name:first').text(__['React']).removeClass('blue red yellow orange');
      /* change reaction-btn-icon */
      _parent.find('.reaction-btn-icon:first').html('<i class="far fa-smile fa-lg fa-fw"></i>');
      /* hide reactions-container */
      _parent.find('.reactions-container:visible').removeAttr('style').hide();
      /* AJAX */
      $.post(api['posts/reaction'], { 'do': _undo, 'reaction': old_reaction, 'id': id }, function (response) {
        /* check the response */
        if (response.callback) {
          eval(response.callback);
        }
      }, 'json')
        .fail(function () {
          modal('#modal-message', { title: __['Error'], message: __['There is something that went wrong!'] });
        });
    } else {
      /* [2] user react */
      /* add unreact class to reactions wrapper */
      if (!reactions_wrapper.hasClass('js_unreact-' + handle)) {
        reactions_wrapper.addClass('js_unreact-' + handle);
      }
      /* change reactions-wrapper data-reaction */
      reactions_wrapper.data('reaction', reaction);
      /* change reaction-btn-name */
      _parent.find('.reaction-btn-name:first').text(reaction_title).removeAttr('style').css("color", reaction_color);
      /* change reaction-btn-icon */
      _parent.find('.reaction-btn-icon:first').html('<div class="inline-emoji no_animation">' + reaction_html + '</div>');
      /* hide reactions-container */
      _parent.find('.reactions-container:visible').removeAttr('style').hide();
      /* AJAX */
      $.post(api['posts/reaction'], { 'do': _do, 'reaction': reaction, 'id': id }, function (response) {
        /* check the response */
        if (response.callback) {
          eval(response.callback);
        }
      }, 'json')
        .fail(function () {
          modal('#modal-message', { title: __['Error'], message: __['There is something that went wrong!'] });
        });
    }
  });
  • sounds like what you are looking for is a “toggle” kind of situations, no?!
  • then i would so maybe on each “click event” look for that “corresponding value” change and make appropriate changes

also, if you have that live, it becomes more easier to see what is it that your are facing and what needs to be done to change it…happy coding :slight_smile:

As an example: So you have a button. As well as whatever other markup it needs, give it an aria-pressed attribute to cover accessibility and a class for targeting it in JS and CSS:

<button
  type="button" 
  class="reaction-toggle"
  aria-pressed="true"
  aria-label="Some description of what this does"
 >
  +
</button>

You then listen out for click events. When one happens, find the button that triggered the event then toggle that aria attribute:

document.addEventListener("click", (clickevent) => {
  const target = clickevent.target;

  if (target.hasClass("reaction-toggle")) {
    switch (target.getAttribute("aria-pressed") {
      case "true": target.setAttribute("false"); break;
      case "false": target.setAttribute("true"); break;
      default: throw new Error("Reaction toggle must have an aria-pressed attribute");
    }
  }
});

Then style the buttons however you want based on it’s state
.

.reaction-toggle {
  color: black;
}

. reaction-toggle[aria-pressed="true"] {
  color: pink;
}