2

I'm using javascript to show/hide content. I have a link under each content section. When a user clicks the link [show] it shows all content. When the user clicks the link again it hides the content. I would like the link text to change from show to hide, and hide to show on click. Here's an example of what I'm looking to do.

I've managed to solve the show/hide part, but I'm having issues with changing the link text. It only works for Content 1, and not the other parts. I assume this is because querySelectorAll("button")[0] finds all instances of a.toggle and picks the first occurrence from the array [0]. I don't know how to apply the javascript to all instances. Thanks.

HTML

<div>
  <h1>Title</h1>      
  <div>Content - Show</div>
  <div id="section" style="display:none;">
    <div>Content - Hide</div>
  </div>
  <div><a class="toggle" href="javascript:divHideShow('section');" data-text-swap="Hide">Show</a></div>
</div>

<div>
  <h1>Title 2</h1>      
  <div>Content 2 - Show</div>
  <div id="section2" style="display:none;">
    <div>Content 2 - Hide</div>
  </div>
  <div><a class="toggle" href="javascript:divHideShow('section2');" data-text-swap="Hide">Show</a></div>
</div>


<div>
  <h1>Title 3</h1>      
  <div>Content 3 - Show</div>
  <div id="section3" style="display:none;">
    <div>Content 3 - Hide</div>
  </div>
  <div><a class="toggle" href="javascript:divHideShow('section3');" data-text-swap="Hide">Show</a></div>
</div>

JS

 var toggle = document.querySelectorAll("a.toggle")[0];
toggle.addEventListener('click', function() {
  if (toggle.getAttribute("data-text-swap") == toggle.innerHTML) {
    toggle.innerHTML = toggle.getAttribute("data-text-original");
  } else {
    toggle.setAttribute("data-text-original", toggle.innerHTML);
    toggle.innerHTML = toggle.getAttribute("data-text-swap");
  }
}, false);

function divHideShow(divToHideOrShow) {
  var div = document.getElementById(divToHideOrShow);
    if (div.style.display == "none") {
      div.style.display = "block";
    }
    else {
      div.style.display = "none";
    }
  }     
  • What is the code that you are using? Above pair or the below one? – void Feb 23 '18 at 20:27
  • I'm using both. Above is to change text on the button - this is what I need help with. Below is to show/hide content. –  Feb 23 '18 at 20:28
  • I guess you want to toggle the html of the clicked button based on if the div is hidden or not, but your HTML of above and below code is different? Isnt it should be same? – void Feb 23 '18 at 20:31
  • I agree with the other commenters. Please put all of your relevant HTML and CSS into the same code blocks. – ecg8 Feb 23 '18 at 20:32
  • Sorry, will do. Give me a minute and I'll update the code. Sorry, my bad. –  Feb 23 '18 at 20:35
  • I've updated the code and question. I hope this makes things clearer. –  Feb 23 '18 at 20:42

1 Answers1

1

The easiest way to do this would be to bind a listener to a container element of your elements you want to show/hide. Then inside the listener check if its the right target. Then swap the textContent and look for a target to change the display style on.

var ToggleContent = (function(doc) {
  var targetMap = {}, initialized;
  function init(containerSelector) {
    // make sure the plugin is only initialized once
    if(initialized) return;
    // find container element
    var container = document.querySelector(containerSelector) || doc.body;
    
    // bind 'click'-listener to container element
    container.addEventListener('click', function(event) {
      // declare target as the clicked element
      var target = event.target;
      
      // if clicked element has the 'data-toggle-text' attribute
      if (target.dataset.toggleText) { 
        // swap the text contents
        var tmp = target.textContent;
        target.textContent = target.dataset.toggleText;
        target.dataset.toggleText = tmp;

        // if clicked element has the 'data-toggle-for' attribute
        if (target.dataset.toggleFor) {

          // lookup target to toggle visibility
          target = targetMap[target.dataset.toggleFor];

          // if such a target exists
          if(target) {
            // toggle display property to hide/show element
            target.style.display = target.style.display ? null : 'none';
          }
        }
      }
    });
    update();
    initialized = true;
  }

  function update() {
    // find all elements with the 'data-toggle-target' attribute
    var elements = [].slice.call(document.querySelectorAll('[data-toggle-target]'));
    
    // save the result into a simple object map to lookup elements later on
    targetMap = elements.reduce(function(o, el) {
      o[el.dataset.toggleTarget] = el;
      return o;
    }, {});
  }
  
  // return public methods
  return {
    init: init,
    update: update
  };
})(document);

ToggleContent.init();
body {
  font-family: sans-serif;
}

a {
  font-weight: bold;
  cursor: pointer;
}
<div>
  <h1>Title</h1>      
  <div>Content - Show</div>
  <div data-toggle-target="content1" style="display:none;">
    <div>Content - Hide</div>
  </div>
  <div><a class="toggle" data-toggle-for="content1" data-toggle-text="Hide">Show</a></div>
</div>

<div>
  <h1>Title 2</h1>      
  <div>Content 2 - Show</div>
  <div data-toggle-target="content2" style="display:none;">
    <div>Content 2 - Hide</div>
  </div>
  <div><a class="toggle" data-toggle-for="content2" data-toggle-text="Hide">Show</a></div>
</div>


<div>
  <h1>Title 3</h1>      
  <div>Content 3 - Show</div>
  <div data-toggle-target="content3" style="display:none;">
    <div>Content 3 - Hide</div>
  </div>
  <div><a class="toggle" data-toggle-for="content3" data-toggle-text="Hide">Show</a></div>
</div>

Edit: If you add some elements dynamically just call the update method on ToggleContent.

Also this could be easily made more dynamic to even work for selectors and multiple targets to hide/show.

cyr_x
  • 13,987
  • 2
  • 32
  • 46
  • That works beautifully. Thanks boss! I'll play around with this to figure out how everything works. –  Feb 23 '18 at 20:59
  • Your welcome, it could be optimized a little bit but have fun playing around ;) – cyr_x Feb 23 '18 at 21:00
  • I've added some comments to the code to make it better to understand. – cyr_x Feb 23 '18 at 21:06
  • Thanks for adding comments. I'm a few weeks into learning to code so your help and others on here is invaluable. vielen Dank! :D –  Feb 23 '18 at 23:39
  • Quick question please. Any reason why the code you provided does not work on iOS? It works flawlessly in Chrome and Safari, but not iOS Safari. –  Feb 24 '18 at 23:31
  • The Show/Hide link appears just as text, not a link, so it can't be clicked –  Feb 24 '18 at 23:33
  • It could be because of the click listener , on mobile click isn't available – cyr_x Feb 24 '18 at 23:33
  • You're right. I just added
    and it worked. Thanks for pointing me in the right direction. https://stackoverflow.com/questions/14054272/click-event-listener-works-in-safari-in-osx-but-not-in-ios
    –  Feb 24 '18 at 23:36
  • I would recommend to take a look at https://hammerjs.github.io/ because it provides an easy way to translate desktop events into mobile events and the other way around – cyr_x Feb 24 '18 at 23:37
  • Will do. Thanks. –  Feb 24 '18 at 23:38
  • Also your suggested solition via `onclick=""` is a very hacky solution and won't work in certain mobile browsers – cyr_x Feb 24 '18 at 23:39
  • Understood. I'll look at the link you sent and see if I can come up with something better. –  Feb 24 '18 at 23:42
  • And if you want to somehow develop an web application which uses a lot of javascript i would recommend you to use a library like `react` or `vuejs` or even a framework like `angular` (angular !== angularJS) – cyr_x Feb 24 '18 at 23:43
  • Appreciate the recommendation. –  Feb 25 '18 at 00:13