Jump to content

HideShow Toggle


DarkxPunk

Recommended Posts

Hey everyone,

 

So I know how to do a basic HideShow Toggle for a element that is a direct sibling, but what I need is to find the next element with a class of "hideShow". I am still not completely understanding of all the DOM and how to use JS to traverse it, so any help is appreciated.

 

Thanks.

 

P.S. I don't wanna use jQuery...

Link to comment
Share on other sites

...what I need is to find the next element with a class of "hideShow". [not using jQuery]

 

 

How did you begin? Is this an arbitrary element in the page? Is the element you want find a particular type of element or can it be anything? Do you want to do something to all such elements at one time, or only the next one?

Edited by davej
Link to comment
Share on other sites

First off let me preface with if I understood how to traverse HTML DOM it would be easy as pie...

 

So for styling purposes I have a div inside a div... Lets call the outside div DivWrap, and the internal one called DivContent... Now I duplicate this again underneath looking like this:

 

DivWrap

DivContent

DivWrap

DivContent

 

Now the second DivWrap is hidden by default, and the link to expose it will be in the first DivContent. Now I don't want to create multiple showHide functions for each iteration of this concept, especially when I will have multiple of these on a page. What I require is that when I click the link it will search from that point for the closes Div with the class showHide and simply apply the display:block.

 

Now I know how to do it when its a direct child, but not when its so spread like this.

 

Hope that helps.

Thanks.

 

(If I figure it out before you reply I will post it :D )

Link to comment
Share on other sites

Like this?

<div class="DivWrap clickable"><div class="DivContent"><p>Click here</p></div></div><div class="DivWrap showhide"><div class="DivContent"><p>Happy Birthday Fred!</p></div></div>
Edited by davej
Link to comment
Share on other sites

<div class="DivWrap"><div class="DivContent clickable"><p>Click here</p></div></div><div class="DivWrap showhide"><div class="DivContent"><p>Happy Birthday Fred!</p></div></div>

The DivWraps are 100% width, I only want the div content clickable.

Link to comment
Share on other sites

Do you want to keep to this exact format or do you want to be able to search all the way to the end of the document? Also do we only search div elements? Also do you want to search at click-time or do you want to store the relationships at page-load for later use?

Edited by davej
Link to comment
Share on other sites

Well I would like to understand traversing HTML DOM better so I can manipulate it as I need. But in the mean time all I require is this format... I will use it multiple time, but it will just apply to the closest element. Preferably I would like it to work with more than just div elements... Lastly the search at click-time should be fine... Makes it more versatile right?

Link to comment
Share on other sites

Well, here is one approach...

<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><title>Showhide next element with class showhide</title><style>.clickable{text-decoration: underline;cursor: pointer;}.showhide{color: red;background-color: yellow;display: none;}</style><script>window.onload = init;function init(){var iclass;var cnt = 0;var list = document.getElementsByTagName('*');//obtain list of all elementsfor (var i=0 ; i<list.length ; i++){iclass = list[i].className;if (iclass.indexOf('clickable')!=(-1)){list[i].onclick = showhide;cnt++;}}}// end of funcfunction showhide(){var list = document.getElementsByTagName('*');//obtain list of all elementsvar done = false;var iclass, idx;var i = 1;while (list[i]!==this){ //search for current divi++;}i++;do{iclass = list[i].className;if (iclass.length>0){idx = iclass.indexOf('showhide');}else{idx = -1;}if(idx>=0){done=true;var disp = list[i].style.display;if(disp == 'none' || disp == ''){ list[i].style.display = 'block';}else{ list[i].style.display = 'none';}}i++;}while (!done && i<list.length);}//end of func</script></head><body><div class="DivWrap"><div class="DivContent"><p>misc content</p></div></div><div class="DivWrap"><div class="DivContent clickable"><p>Click here to show/hide</p></div></div><div class="DivWrap showhide"><div class="DivContent"><p>Happy Birthday!</p></div></div><div class="DivWrap"><div class="DivContent"><p>misc content2</p></div></div><footer><hr/><p style="text-align:center">©Copyright notice</p></footer></body></html>

This will require some testing. It searches down the document tree from the point of the clickable element to find the showhide element. Of course absolute positioning of those elements could be incompatible with this search scheme. This simple of an example case looks a little crazy because it would seem that a simpler approach could have been used if the clickable item and the showhide item had been grouped together.

Edited by davej
Link to comment
Share on other sites

Works great!

 

But I feel there must be a better solution... Maybe its my miss understanding of JS, but can you not traverse HTML DOM from a element?

 

For example:

 

onclick run function hideshow

 

hideshow takes the element that has the on click with that function than leaves that elements and moves down the page from its position till it finds a classname hideshow then applies display: none or display: block.

 

In other words without first collecting all elements and their classes...

 

I will use this script in the mean time, but will keep looking for a way to skim it down. Thanks allot.

Link to comment
Share on other sites

Yes, there is another approach but I thought this would be easier especially when I thought you only wanted to handle divs. Now that I know you want to handle any element the other approach might be worth a try.

 

You can certainly assign the handler function manually, but you'll then you need to assign an id, or you can mix Javascript into the html but that is increasingly considered bad form.

Edited by davej
Link to comment
Share on other sites

I guess I just don't understand how it simply finds the "next" div with the correct class when all you are doing is throwing all that is found in an array. But I will play and see if I can grasp it.

Link to comment
Share on other sites

Yes, it was more efficient when it was only making a list of divs rather than a list of everything. Anyway it obtains the list and then it finds the position of the calling clickable element, and then it searches from that point downward to find the first showhide element. This approach could also be made more efficient if we limited the list to the descendants of the first common ancestor.

 

The other approach involves searching for children, and grand-children, all the way down... and then searching for siblings, and then finding uncles or grand-uncles or great-grand-uncles, and all their descendants, which is more complex.

Edited by davej
Link to comment
Share on other sites

Each element has properties such as nextSibling, previousSibling, parentNode, childNodes etc that you can use to traverse the DOM. If you were writing something for the structure described in post 3, and the click event was on the first content div, then the next wrap div would be element.parentNode.nextSibling.

Link to comment
Share on other sites

Well the solution you provided worked because I can apply it to the entire div and I think I can simplify it... But still this is great to explore to expand my understanding.

Link to comment
Share on other sites

The child/sibling/parent traversal logic is actually looking much more trivial than I expected.

<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><title>Showhide next via traversal</title><style>.clickable{text-decoration: underline;cursor: pointer;}.showhide{/*visibility: hidden;*/color: red;background-color: yellow;display: none;}</style><script>window.onload = init;function init(){var iclass;var list = document.getElementsByTagName('*');for (var i=0 ; i<list.length ; i++){iclass = list[i].className;if (iclass.indexOf('clickable')!=(-1)){list[i].onclick = showhide;}}}// end of funcfunction showhide(){// this version will do a traditional traversevar node = this;var done = false;var iclass;var childrenDone = false;do{if (node.hasChildNodes() && !childrenDone){node = node.firstChild;childrenDone = false;}else if(node.nextSibling!=null){node = node.nextSibling;childrenDone = false;}else if(node.parentNode.nodeName!='BODY'){node = node.parentNode;childrenDone = true;}else{done = true;}iclass = node.className;if(iclass!=null && iclass.length>0){idx = iclass.indexOf('showhide');}else{idx = -1;}if(idx>=0){done=true;var disp = node.style.display;if(disp == 'none' || disp == ''){ node.style.display = 'block';}else{ node.style.display = 'none';}}}while (!done);}//end of func</script></head><body><div class="DivWrap"><div class="DivContent"><p>misc content!</p></div></div><div class="DivWrap"><div class="DivContent clickable"><p>Click here to <span>show/hide</span></p></div></div><div class="DivWrap showhide"><div class="DivContent"><p>Happy Birthday!</p></div></div><div class="DivWrap"><div class="DivContent"><p>misc content</p></div></div><footer><hr/><p style="text-align:center">©Copyright notice</p></footer></body></html>
Edited by davej
Link to comment
Share on other sites

So I have been playing around and going over, trying to implement my old toggle, but there are a few factors I keep forgetting to consider. First off my basic toggle is as follows:

function hideShow(tag) {var element = tag;while (element.className != 'hideShow') {element = element.nextSibling;}element.style.display = element.style.display == 'none' ? 'block' : 'none';}

My page structure has changed to this:

<div id="mainWrap"> <div id="styledWrap" onclick="hideShow(this)">  <div id="contentWrap">   <p>Content</p><div class="changeContent">Show(Change to hide when hideShow styleWrap is clicked)</div>  </div> </div> <div id="styledWrap" class="hideShow">  <div id="contentWrap">   <p>Content</p>  </div> </div></div>

Now I don't know how to properly get that text area to change and for some reason I can't get the basic toggle to work. Will keep playing.

 

-EDIT-

 

So I resolved the JS and made it fit:

function showHide(div) {var element = div.nextSibling.nextSibling;element.style.display = element.style.display == 'none' ? 'block' : 'none';}

Still need to wrap my head around traversing HTML DOM, but getting there.

 

-EDIT 2-

 

Now I think I have it:

function showHide(div) {var element = div.nextSibling.nextSibling;var viewHide = div.childNodes[1].childNodes[3].childNodes[6];element.style.display = element.style.display == 'none' ? 'block' : 'none';viewHide.innerHTML = viewHide.innerHTML == 'See Examples...' ? 'Hide Examples...' : 'See Examples...';}

Now that was allot of Node searching, maybe there is a better way? But so far so good.

Edited by DarkxPunk
Link to comment
Share on other sites

I'm not sure I really understand what your div structure does, so I'm a little confused. You aren't really searching -- you are specifying particular relationships. What you could consider is simply passing the id that you wish to show/hide. That would be very simple and straightforward...

<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><title>Hideshow by passing id</title><style>.clickable{cursor: pointer;text-decoration: underline;}.changeContent{color: red;background-color: yellow;display: none;}</style><script>function hideShow(id){var target = document.getElementById(id);target.style.display = target.style.display != 'block' ? 'block' : 'none';}</script></head><body><div id="mainWrap">  <div class="clickable" onclick="hideShow('hs22')">    <p>Clickable Content</p>     <div class="contentWrap">      <p>Clickable Content styled further by contentWrap</p>    </div>  </div>  <div class="changeContent" id="hs22">    <p>Content that hides</p>    <div class="contentWrap">      <p>Content that hides styled further by contentWrap</p>    </div>  </div></div><footer><hr/><p style="text-align:center">©Copyright notice</p></footer></body></html>
Edited by davej
Link to comment
Share on other sites

If the divs are not added dynamically then you could still manually assign ids. Or if the div structure always repeats then you could use a fixed traversal relationship.

Link to comment
Share on other sites

Am I not doing the second thing?

 

Actually, yes, but it looked wrong and now I see why -- the darn white-space creates a text node. Also comments add nodes. That makes a fixed approach an unreliable method. Maybe a minimal search would be better...

<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><title>Showhide next sibling div</title><style>.clickable{cursor: pointer;text-decoration: underline;}.hideShow{color: red;background-color: yellow;display: none;}</style><script>function hideShow(div) {var node = div.nextSibling;while(node!=null && node.nodeName!='DIV'){node = node.nextSibling;}var iclass = node.className;if(iclass.indexOf('hideShow')!=(-1)){node.style.display = node.style.display != 'block' ? 'block' : 'none';}}</script></head><body><div id="mainWrap">  <div class="styledWrap" onclick="hideShow(this)">    <div class="contentWrap">      <p>Content</p>      <div class="changeContent">        Show(Change to hide when hideShow styleWrap is clicked)      </div>    </div>  </div>  <div class="styledWrap hideShow">    <div class="contentWrap">      <p>Content</p>    </div>  </div></div><footer><hr/><p style="text-align:center">©Copyright notice</p></footer></body></html>

This searches for the next sibling div and then if that div has a class of hideShow it toggles the display mode. It does not search beyond the first sibling div or search child or parent nodes.

Edited by davej
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...