0

I have a function written to open / close a div using a placeholder for a hyperlink.

<a class="button" onclick="show('click-drop')">Open</a>

The onclick event then opens the div.

I have this onclick to close it:

<a class="button" onclick="hide('click-drop')">Close</a>

What I would like to do is have a single placeholder for a hyperlink that switches between the two onclick events. So onclick=show would switch to onclick=hide and vice versa.

I have looked everywhere and cannot find a straightforward solution for my situation.

Revised Question

Here's specifically what I am working with.

function show(target) {
    document.getElementById(target).style.height = '300px';
}
function hide(target) {
    document.getElementById(target).style.height = '0px';
}

<a class="button" onclick="show('click-drop')">Open</a>
<div id="click-drop" style="height:0px">
    <a class="button" onclick="hide('click-drop')">Close</a>
</div>

Click Open and it opens / expands inline height style. Click Close and it closes.

I need to have the onclick toggle, but with Open changing to Close and reverse.

It would be ideal to keep the placement of the links. So the Open link outside the link is hidden until the Close link within the div is clicked.

I appreciate any feedback.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Jarod Thornton
  • 401
  • 1
  • 7
  • 24

6 Answers6

2

You could dynamically change the onclick event of the button, but it's already awkward enough that you're using HTML to describe functionality. It would do you even better if you kept all functionality in the JavaScript. Something like this:

var button = document.getElementById("button");
var target = document.getElementById("target");

button.innerHTML = "Close";
button.onclick = function() { hide(target); };

function hide(target) {
  console.log("hiding");
  target.style.display = "none";
  console.log(target);
  button.innerHTML = "Open";
  button.onclick = function() { show(target); };
}

function show(target) {
  target.style.display = "block";
  button.innerHTML = "Close";
  button.onclick = function() { hide(target); };
}
<button id="button">Close</button>
<div id="target">Hello World!</div>
4castle
  • 32,613
  • 11
  • 69
  • 106
  • I've posted a second answer if you want more reusability out of `hide` and `show`. I think it may even be a better answer than this first answer, but I'll let the community decide. – 4castle May 15 '16 at 04:40
  • So I am reading up a little on what you've suggested. Because I am not using unobtrusive JS I am limited in some aspects. But it's a little above my head ATM. I can't run a jsfiddle because of this reason :/ – Jarod Thornton May 15 '16 at 04:54
  • @jarmerson I've made my code into a snippet so that you can run it, if that is what you mean. – 4castle May 15 '16 at 05:12
  • My answer below will clarify what I meant. I'm referencing my own code. – Jarod Thornton May 15 '16 at 05:13
  • @jarmerson Ohh, I think I understand now. To use JS in the HTML in a jsfiddle, use a – 4castle May 15 '16 at 05:39
  • That doesn't seem to work with my specific example - https://jsfiddle.net/jarmerson/c43xxeLx/3/ - I've tried several variations of options there too. Obviously this is new to me. – Jarod Thornton May 15 '16 at 05:47
  • Here's what I meant: I modified [your JSFiddle](https://jsfiddle.net/c43xxeLx/5/) – 4castle May 15 '16 at 05:56
  • Ahh, I see. Thank you. I will tackle these other two things after some shut eye. – Jarod Thornton May 15 '16 at 05:57
2

Here is another answer in case you want to use toggle on multiple buttons. This solution uses the button to store the state of its target, and so it uses toggle.call() to modify the value of this within toggle.

It's inspired by jQuery.toggle() and Yuriy Yakym's answer.

for (i = 1; i <= 3; i++) {
  var button = document.getElementById("button"+i),
      target = document.getElementById("target"+i);
  // wrapped in closure because https://stackoverflow.com/q/8909652/5743988
  (function(_button, _target) {
    _button.onclick = function() {
      toggle.call(_button, _target);
    }
  })(button, target);
}

function toggle(target) {
  this.func = this.func || hide; // if "func" is undefined, assign "hide"
  this.func.call(target); // execute "func"
  this.func = this.func === hide // make "func" call the other thing next time
    ? (this.innerHTML = "Show", show) : (this.innerHTML = "Hide", hide);
}

function hide() {
  this.oldDisplay = window.getComputedStyle(this).display;
  this.style.display = "none"; // better than "height = 0px"
}

function show() {
  this.oldDisplay = this.oldDisplay || "block";
  this.style.display = this.oldDisplay;
}
button, div {
  display: inline-block;
}
<button id="button1">Close</button>
<div id="target1">Hello World!</div>
<br>
<button id="button2">Close</button>
<div id="target2">Hello Again World!</div>
<br>
<button id="button3">Close</button>
<div id="target3">Hello Again Again World!</div>
Community
  • 1
  • 1
4castle
  • 32,613
  • 11
  • 69
  • 106
  • I appreciate your insight @4castle. The answers here helped. I did figure out a hack of sorts that works perfectly, but may be improved upon. See my answer below. Maybe you can help with the two questions it raises. Thanks! – Jarod Thornton May 15 '16 at 05:09
1

It's possible to save button's state inside its context. And then, depending on its state call appropriate functions.

function toggle() {
    this.state = !this.state; // false - open; true - closed; default = false (because is not initialized yet)
    this.innerHTML = this.state ? 'Close' : 'Open';
    
    if(this.state) {
        open();
    } else {
        close();
    }
}

function open() {
    alert("Opened");
}

function close() {
    alert("Closed");
}
<a class="button" onclick="toggle.call(this)">Open</a>
<a class="button" onclick="toggle.call(this)">Open</a>
<a class="button" onclick="toggle.call(this)">Open</a>
Yuriy Yakym
  • 3,616
  • 17
  • 30
  • 1
    I think you flipped `open` and `close`. Close is supposed to be first I believe. This is really awesome! You might also want to store the target element in the button's context too so that `toggle` is reusable for other buttons without having to repeatedly call `document.getElement...`. – 4castle May 15 '16 at 02:04
  • This works as expected but it wasn't suitable for my specific need. It's pretty cool though - will make use of it for sure. – Jarod Thornton May 15 '16 at 05:11
0

Either the jQuery toggle or the toggleClass methods should the trick for you.

Edit, per requested, an example:

Say you have a CSS class that looks like this, based on the OP:

.info-box { 
    height: 300px;
}

By using $('#myTarget').toggleClass('info-box'), you will be removing that class or adding it. If you don't have it, you'll add it. If you do have it, you'll remove it. Just like that.

So, a working example I just threw together is here, and it implements toggle() by itself without the need to add any classes, which I believe still accomplishes what the OP was seeking resolution for.

https://jsfiddle.net/bbh9m478/1/

Hope that helps!

napo
  • 869
  • 9
  • 19
  • As is, this isn't an answer to the question. At best, it's a link to the answer. Can you elaborate? Or make a comment. – 4castle May 15 '16 at 00:20
  • Yeah, it's hard to write examples when typing on your smartphone. :) Just got to a laptop and added examples and a working jsFiddle. Hope that clarifies a bit! – napo May 15 '16 at 01:05
0

I figured out a straightforward solution for this.

When the open link is clicked and the div opens I need the open link to disappear and a close link displayed as a separate element. The same event should work in reverse i.e. close link closes div and displays open link.

While the original code I am working with uses an inline height property to control the open and close, the example below uses display property.

function show(target) {
    document.getElementById(target).style.display = 'block';
}
function hide(target) {
    document.getElementById(target).style.display = 'none';
}
function hideButton() {
    var x = document.getElementById("open");
    x.style.display = "none";
    var x = document.getElementById("close");
    x.style.display = "";
}
function showButton() {
    var x = document.getElementById("open");
    x.style.display = "";
    var x = document.getElementById("close");
    x.style.display = "none";
}

<a class="button" onclick="show('click-drop');hideButton()" id="open">Open</a>

<div id="click-drop" style="display:none">

<a class="button" onclick="hide('click-drop');showButton()" id="close" style="display:none">Close</a>

</div>              

Here it is in the wild - http://iemajen.com/scripts/mssa/

With this in place I have two more questions.

  1. How would I write this so it uses unobtrusive Javascript to attach behaviour to DOM elements from within the JS solely, meaning separate HTML from JS
  2. How can I write this to be used on multiple divs? I have a dozen divs that will be closed initially and each opened individually using the markup I wrote. I could write it up for each div / unique ids and such, but that seems like more bad practice and I would like to learn the best practice.

Should I just open a new topic for the last question?

Jarod Thornton
  • 401
  • 1
  • 7
  • 24
  • 1
    To answer you first question, see what I did in my first answer. To answer your second question, give additional parameters to `hideButton` and `showButton` so that they will be reusable for many buttons. The parameter you add should be the id of the two buttons, or you can pass the buttons themselves as the parameter. – 4castle May 15 '16 at 05:37
  • @4castle you gave me great direction. I've written it up so it's unobtrusive - https://jsfiddle.net/jarmerson/e2fexbqp/ - now I am unclear on how to give additional parameters so I can use the buttons in multiple instances along side an equal number of divs. I've played around without luck. – Jarod Thornton May 15 '16 at 22:35
0

After you create your element

var elem = document.querySelector('.js-switch'); var init = new Switchery(elem);

Bind your OnClickMetod to init.switcher like this

$(init.switcher).on('click', OnClickMetod);

FriendsKenny
  • 150
  • 3
  • 11