I want to make an expandable menu, with three or more items. When one of these items is clicked on, it expands to show an <ul>
that is display: hidden
beneath it.
Furthermore, I want it to work in a way that if one of the items on that menu is already expanded, if I click on any other one of the items, it retracts the content of the first item before expanding the second item (the one clicked on).
The code I have is this. I had to define a different toggle
function for each of the three items I have there, plus an extra switchAll
function to hide any expanded item before proceeding to expand a new one.
However, that solution does not seem to be the best.
Isn't there a better way to define a function that could handle all the three items? For that I'd need for it to get which of the items is clicked on before, so that it can understand which of them are expanded or not, and what actions to take accordingly.
I'm guessing this could be done with only two functions, but I can't seem to figure out how to do this.
Also, I want to achieve this with Javascript, not jQuery.
The relevant code follows:
HTML:
<ul>
<li><a href="#" onclick="toggle();">one</a>
<ul id="menu">
<li>sacd</li>
<li>safsdf</li>
</ul>
</li>
<li><a href="#" onclick="toggle1();">two</a>
<ul id="menu2">
<li>jlkfd</li>
<li>ljsdf</li>
</ul>
</li>
<li><a href="#" onclick="toggle2();">three</a>
<ul id="menu3">
<li>sfsdvbg gb</li>
<li>asdavffds</li>
</ul>
</li>
</ul>
CSS:
#menu {
display: none;
}
#menu2 {
display : none;
}
#menu3 {
display : none;
}
JS:
var hidden = true;
var hidden2 = true;
var hidden3 = true;
function switchAll(other1, other2) {
other1.style.display = "none";
other2.style.display = "none";
hidden = true;
hidden2 = true;
hidden3 = true;
};
function toggle() {
var menu = document.getElementById("menu");
var menu2 = document.getElementById("menu2");
var menu3 = document.getElementById("menu3");
if(hidden) {
switchAll(menu2, menu3);
menu.style.display = "block";
hidden = false;
} else {
menu.style.display = "none";
hidden = true;
};
};
function toggle1() {
var menu = document.getElementById("menu");
var menu2 = document.getElementById("menu2");
var menu3 = document.getElementById("menu3");
if(hidden2) {
switchAll(menu, menu3);
menu2.style.display = "block";
hidden2 = false;
} else {
menu2.style.display = "none";
hidden2 = true;
};
};
function toggle2() {
var menu = document.getElementById("menu");
var menu2 = document.getElementById("menu2");
var menu3 = document.getElementById("menu3");
if(hidden3) {
switchAll(menu, menu2);
menu3.style.display = "block";
hidden3 = false;
} else {
menu3.style.display = "none";
hidden3 = true;
};
};
I want to make an expandable menu, with three or more items. When one of these items is clicked on, it expands to show an <ul>
that is display: hidden
beneath it.
Furthermore, I want it to work in a way that if one of the items on that menu is already expanded, if I click on any other one of the items, it retracts the content of the first item before expanding the second item (the one clicked on).
The code I have is this. I had to define a different toggle
function for each of the three items I have there, plus an extra switchAll
function to hide any expanded item before proceeding to expand a new one.
However, that solution does not seem to be the best.
Isn't there a better way to define a function that could handle all the three items? For that I'd need for it to get which of the items is clicked on before, so that it can understand which of them are expanded or not, and what actions to take accordingly.
I'm guessing this could be done with only two functions, but I can't seem to figure out how to do this.
Also, I want to achieve this with Javascript, not jQuery.
The relevant code follows:
HTML:
<ul>
<li><a href="#" onclick="toggle();">one</a>
<ul id="menu">
<li>sacd</li>
<li>safsdf</li>
</ul>
</li>
<li><a href="#" onclick="toggle1();">two</a>
<ul id="menu2">
<li>jlkfd</li>
<li>ljsdf</li>
</ul>
</li>
<li><a href="#" onclick="toggle2();">three</a>
<ul id="menu3">
<li>sfsdvbg gb</li>
<li>asdavffds</li>
</ul>
</li>
</ul>
CSS:
#menu {
display: none;
}
#menu2 {
display : none;
}
#menu3 {
display : none;
}
JS:
var hidden = true;
var hidden2 = true;
var hidden3 = true;
function switchAll(other1, other2) {
other1.style.display = "none";
other2.style.display = "none";
hidden = true;
hidden2 = true;
hidden3 = true;
};
function toggle() {
var menu = document.getElementById("menu");
var menu2 = document.getElementById("menu2");
var menu3 = document.getElementById("menu3");
if(hidden) {
switchAll(menu2, menu3);
menu.style.display = "block";
hidden = false;
} else {
menu.style.display = "none";
hidden = true;
};
};
function toggle1() {
var menu = document.getElementById("menu");
var menu2 = document.getElementById("menu2");
var menu3 = document.getElementById("menu3");
if(hidden2) {
switchAll(menu, menu3);
menu2.style.display = "block";
hidden2 = false;
} else {
menu2.style.display = "none";
hidden2 = true;
};
};
function toggle2() {
var menu = document.getElementById("menu");
var menu2 = document.getElementById("menu2");
var menu3 = document.getElementById("menu3");
if(hidden3) {
switchAll(menu, menu2);
menu3.style.display = "block";
hidden3 = false;
} else {
menu3.style.display = "none";
hidden3 = true;
};
};
Share
Improve this question
edited Apr 13, 2014 at 14:35
JNat
asked Apr 13, 2014 at 13:39
JNat♦JNat
1,4964 gold badges36 silver badges38 bronze badges
3
- could you please post the relevant code – caramba Commented Apr 13, 2014 at 13:41
- The indentations are not displayed correctly here, but on JSFiddle they are fine. I can't seem to format it it correctly here. – JNat ♦ Commented Apr 13, 2014 at 13:48
- why don't you use an accordeon plugin? why reinventing the wheel? is there a real need for a custom implementation? – arieljuod Commented Apr 13, 2014 at 14:05
4 Answers
Reset to default 4If my first answer is to plicated here is a css only alternative without animations & js.
html
<ul class="box">
<li id="a"><a href="#a">one</a>
<ul>
<li>sacd</li>
<li>safsdf</li>
</ul>
</li>
<li id="b"><a href="#b">two</a>
<ul>
<li>jlkfd</li>
<li>ljsdf</li>
</ul>
</li>
<li id="c"><a href="#c">three</a>
<ul>
<li>sfsdvbg gb</li>
<li>asdavffds</li>
</ul>
</li>
</ul>
css
.box>li>ul{
display: none;
}
.box>li:target>ul{
display:block;
}
DEMO
http://jsfiddle/vvLu2/
no javascript...
My JSFiddle is here:http://jsfiddle/naokiota/38W8X/12/
This is an example to simplify your function:
HTML
<ul>
<li><a href="#" onclick="toggle(1);" >one</a>
<ul class="submenu">
<li>sacd</li>
<li>safsdf</li>
</ul>
</li>
<li><a href="#" onclick="toggle(2);" >two</a>
<ul class="submenu">
<li>jlkfd</li>
<li>ljsdf</li>
</ul>
</li>
<li><a href="#" onclick="toggle(3);" >three</a>
<ul class="submenu">
<li>sfsdvbg gb</li>
<li>asdavffds</li>
</ul>
</li>
</ul>
JavaScript
function toggle(n) {
var menus = document.getElementsByClassName("submenu");
for(var i=0;i<menus.length;i++){
if((i == (n-1)) && (menus[i].style.display != "block")){
menus[i].style.display = "block";
}else{
menus[i].style.display = "none";
}
}
};
CSS
.submenu {
display: none;
}
Hope this helps.
Store the current expanded item in a variable.
var current = null;
function toggle(element) {
if(current === null) {
// Show 'element'
current = element;
}
else if(element === current) {
// Hide 'current'
current = null;
}
else {
// Show 'element' and hide 'current'
current = element;
}
}
Yes there is a simpler/(nicer && animated) way to write something like that.
- it is animated
- can handle infinite switches
- does not use display none
- does not use loops
- only one eventhandler controls all the elements
- you can apply any type of style
- you can add multiple accordions just by adding the
accordion
class
i wrote this code some time ago and the optimized it and pressed it.
give me some minutes and i post a more readable code... since the i show you one of the final versions .
it's an Accordion.
function h(e){
var p='parentNode',a=e.target,b=a[p],f=48,u='px',y=b.firstChild==a?b[p]:y;
!y.c||(y.c==b||(y.c.style.height=f+u,y.c.x=f)),
y.c=y.c==b?null:b,
a!=b.firstChild||(b.x=b.x>f?f:(f+b.childNodes[1].offsetHeight),
b.style.height=b.x+u)
}
document.getElementById('container').addEventListener('click',h,false);
DEMO
http://jsfiddle/YjCbM/1/
Here is another version
function handler(e){
e=e||window.event;
var target=e.target||e.srcElement;
var action=target.dataset['action']||'no action';
!(action=='toggle')||(target.childNodes[1].classList.toggle('show'));
!target.dataset['url']||alert(target.dataset['url']);
}
var firstUL=document.getElementsByTagName('ul')[0];
firstUL.addEventListener('click',handler,false);
http://jsfiddle/Jj6FY/1/
Readable Verions (slightly different)
this is basically an accordion plugin
(function(D){
function acsf(e){
var a=e.target.parentNode,h;
if(a.parentNode==this&&a.firstChild==e.target){
if(this.c==a){
this.c.style.height='';
delete this.c
}else{
if(this.c){
this.c.style.height=''
}
this.c=a;
this.c.style.height=(a.firstChild.offsetHeight+
this.c.childNodes[1].offsetHeight)+'px'
}
}
}
function acsinit(){
var acs=D.getElementsByClassName('accordion'),acsl=acs.length;
while(acsl--){
acs[acsl].addEventListener('click',acsf,false);
}
window.removeEventListener('load',acsinit,false);
}
window.addEventListener('load',acsinit,false);
})(document)
css for the animation.
#container>div{
width:512px;height:48px;overflow:hidden;
-webkit-transition:all 300ms ease;
background-color:rgba(0,0,0,0.5);
}
#container>div>div:nth-child(1){
line-height:48px;text-align:center;
background-color:rgba(0,0,0,0.5);
}
html structure
<div id="container" class="accordion">
<div>
<div>Title</div>
<div>description</div>
</div>
<div>
<div>Title</div>
<div>description</div>
</div>
</div>
And if you want to be able to close them all then you need to use loops to check if they are toggled or not.
something like that:
function openAll(){
var elements=container.childNodes,l=elements.length;
while(l--){
if(elements[l].classList.contains('hidden')){
elements[l].classList.remove('hidden');
}
}
}
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744311241a4567956.html
评论列表(0条)