javascriptjqueryhideshow

how to toggle div in jquery with unicode arrow only


I am trying to toggle a div with an arrow; this is the code I have so far:

$('.hide-show').hide();
$('.toggle-year').on('click', function() {
  $(this).html($(this).text().substr(0, 4) == 'Hide' ? '▼' : 'Hide ▲');
  $('.hide-show').toggle('slow');
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<a href="javascript:void(0)" class="toggle-year">&#9660;</a>
<div class="hide-show">
  Content comes here
</div>

This example works fine but needs the text Hide to change the arrow back to downwards. How can I strip out the text Hide (so arrows only?)

Fiddle


Solution

  • The easiest way is to remove all the text that you don't want, and then just compare whether the text is currently-equal to either the up, or down, arrow:

    // hides the text to be hidden on page-load:
    $('.hide-show').hide();
    
    // selects all elements matching the supplied CSS selector
    // and binds the anonymous function of the on() method
    // as the event-handler for the 'click' event:
    $('.toggle-year').on('click', function() {
      $(this).text(function(_, currentText) {
    
      // _ is the index of the current element amongst
      // all the elements retrieved in the jQuery collection;
      // currentText is a reference to the current textContent
      // of the current element from the jQuery collection.
    
    
      // here we use a conditional operator to update the
      // text of the current element. If the currentText is
      // equal to '▼' then we change it to '▲' and if it
      // is not equal to '▼' then we set it to '▼':
        return currentText == "▼" ? "▲" : "▼";
      });
    
      // and then we toggle the visibility of the hidden
      // element(s):
      $('.hide-show').toggle('slow');
    });
    /* we're using a <button> instead of an <a> as we're not
       navigating to a new area of the website, so here we're
       styling it to appear similar to an <a> (assuming that's
       your preference): */
    button.toggle-year {
      border: 0;
      background-color: inherit;
      text-decoration: underline;
      color: #00f;
      outline: 0;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    
    <!-- as mentioned in the CSS, above, because we're not using
         the element for navigation but instead for functionality,
         I've replaced the <a> with a <button>: -->
    <button class="toggle-year">&#9660;</button>
    <div class="hide-show">
      Content comes here
    </div>

    JS Fiddle demo.

    Anything that can be achieved with jQuery can, of course, be similarly achieved by native JavaScript; with that in mind the following shows one means by which plain JavaScript might be used instead:

    // I cache a reference to the document as 'D' because I'm lazy and don't want
    // to type 'document' more than I have to:
    const D = document,
      // here we define the toggleVisibility function, using Arrow function syntax
      // since we don't really need a reference to 'this', and we get an indirect
      // access via the Event Object passed from EventTarget.addEventListener()
      // function later:
      toggleVisibility = (e) => {
      
        // e.target is the element that initiated the click action (in this
        // instance e.currentTarget would also have worked):
        const clicked = e.target,
        
          // caching a reference to the current textContent of that clicked
          // element:
          text = clicked.textContent,
          
          // caching a reference to the element(s) that we intend to show/hide:
          elementsToHide = D.querySelectorAll('.hide-show'),
          
          // we're using this test in two places, so cache the result of the
          // comparison/assessment:
          shouldChange = text == '▼';
    
        // iterating over the collection of elements we found earlier, using
        // NodeList.protoype.forEach():
        elementsToHide.forEach(
          // again using an Arrow function syntax:
          (el) => {
          
            // if the shouldChange variable is true/truthy (in this case it's
            // Boolean true, but it would also work with truthy/falsey values)
            // we change the display of the current Node in the NodeList to
            // 'block', otherwise if shouldChange is false then we set the
            // display to 'none' in order to hide it:
            el.style.display = shouldChange ? 'block' : 'none';
            
            // here if shouldChange is true we update the text-content to
            // '▲', otherwise we change it to '▼':
            clicked.textContent = shouldChange ? '▲' : '▼';
          });
      },
      
      // here we retrieve the <button> elements with the class-name of 'toggle-year':
      toggleButtons = D.querySelectorAll('button.toggle-year');
    
    // and then use NodeList.prototype.forEach() to iterate over that collection:
    toggleButtons.forEach(
      // again, we use Arrow syntax and bind the toggleVisibility() function (but
      // do note the deliberate lack of parentheses below) as the event-handler for
      // the 'click' event:
      (button) => button.addEventListener('click', toggleVisibility)
    );
    button.toggle-year {
      border: 0;
      background-color: inherit;
      text-decoration: underline;
      color: #00f;
      outline: 0;
    }
    
    .hide-show {
      display: none;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    
    <button class="toggle-year">&#9660;</button>
    <div class="hide-show">
      Content comes here
    </div>

    JS Fiddle demo.

    References: