So I am trying to make a dropdown. It works fine except for when I click on one to expand and then click on the other, then when I make a selection on one of them both close. What is the work around this as well as well as what is the best practice/approach for this situations.
var extend = function(){ if(!arguments.length) return {}; else if (arguments.length == 1) return arguments[0]; var primary = arguments[0]; for(var v = 1; v < arguments.length; ++v){ for(prop in arguments[v]){ primary[prop] = arguments[v][prop]; } } return primary; }; var Dropdown = (function(){ self = undefined; Dropdown.instances = []; function Dropdown(element, options){ self = this; this.settings = extend(this.defaults, options); console.log(this.settings); this.element = this.getElement(element); this.trigger = this.getElement(options.trigger); if(!this.element) throw new Error('No element found.'); if(!this.trigger) throw new Error('No trigger found.'); if(!this.settings.optionSelector) throw new Error('Option Selector Not Defined.'); if(this.element.dropdown) throw new Error('Dropdown already exists.'); this.element.dropdown = this; Dropdown.instances.push(this); this.attachTriggerListener = function(event, trigger, dropdown){ trigger.addEventListener(event, function(e){ e.stopPropagation(); dropdown.classList.remove('hidden'); dropdown.classList.add('visible'); }, false); document.addEventListener('click', function(e){ dropdown.classList.remove('visible'); dropdown.classList.add('hidden'); trigger.innerHTML = e.target.innerHTML; }, false); }; this.init(); } Dropdown.prototype.defaults = { css: '', optionSelector: undefined, trigger: undefined, triggersOn: 'click', onShow: function(){}, onClose: function(){}, }; Dropdown.prototype.init = function(){ this.element.classList.add('g-dropdown'); this.element.classList.add('hidden'); this.attachTriggerListener(this.settings.triggersOn, this.trigger, this.element); }; Dropdown.prototype.getElement = function(object){ if(typeof object == 'object' && object instanceof HTMLElement) return object; else if(typeof object == 'string') return document.querySelector(object); }; return Dropdown; }()); var dropdown = new Dropdown('#select', { optionSelector: 'li', trigger: '#trigger' }); var dropdown = new Dropdown('#select2', { optionSelector: 'li', trigger: '#trigger2' });
.g-dropdown{ max-height: 100px; max-width: 75px; overflow: scroll; -webkit-transition: visibility 0.50s, height 0.50s; -moz-transition: visibility 0.50s, height 0.50s; transition: visibility 0.50s, height 0.50s; } .g-dropdown.visible{ visibility: visible; height: 100px; } .g-dropdown.hidden{ visibility: hidden; height: 0px; }
<button id='trigger'>Click Me</button> <ul id='select' style='padding: 0; margin: 0;'> <li>One-one</li> <li>Two-two</li> <li>Three-three</li> <li>Four-four</li> <li>Five-five</li> </ul> <br/><br/><br/> <button id='trigger2'>Click Me</button> <ul id='select2' style='padding: 0; margin: 0;'> <li>One-one</li> <li>Two-two</li> <li>Three-three</li> <li>Four-four</li> <li>Five-five</li> </ul>
So how would I have to modify it so it also closes the dropdown on clicking outside the dropdown?
Try checking e.target.parentElement.nodeName
, this.element.nodeName
at if
condition within document.addEventListener
handler , adding , removing class
of dropdown
if parent element is ul
, else adjusting class
of clicked li
parent element ul
document.addEventListener('click', function(e){
if (e.target.parentElement.nodeName !== this.element.nodeName) {
dropdown.classList.remove('visible');
dropdown.classList.add('hidden');
} else {
e.target.parentElement.classList.remove('visible');
e.target.parentElement.classList.add('hidden');
}
self.settings.onClose(e.target);
}.bind(this), false);
var extend = function(){ if(!arguments.length) return {}; else if (arguments.length == 1) return arguments[0]; var primary = arguments[0]; for(var v = 1; v < arguments.length; ++v){ for(prop in arguments[v]){ primary[prop] = arguments[v][prop]; } } return primary; }; var Dropdown = (function(){ self = undefined; Dropdown.instances = []; function Dropdown(element, options){ self = this; this.settings = extend(this.defaults, options); console.log(this.settings); this.element = this.getElement(element); this.trigger = this.getElement(options.trigger); if(!this.element) throw new Error('No element found.'); if(!this.trigger) throw new Error('No trigger found.'); /* if(!this.settings.optionSelector) throw new Error('Option Selector Not Defined.'); */ if(this.element.dropdown) throw new Error('Dropdown already exists.'); this.element.dropdown = this; Dropdown.instances.push(this); this.attachTriggerListener = function(event, trigger, dropdown){ trigger.addEventListener(event, function(e){ e.stopPropagation(); dropdown.classList.remove('hidden'); dropdown.classList.add('visible'); }, false); document.addEventListener('click', function(e){ if (e.target.parentElement.nodeName !== this.element.nodeName) { dropdown.classList.remove('visible'); dropdown.classList.add('hidden'); } else { e.target.parentElement.classList.remove('visible'); e.target.parentElement.classList.add('hidden'); } self.settings.onClose(e.target); }.bind(this), false); }; this.init(); } Dropdown.prototype.defaults = { css: '', optionSelector: undefined, trigger: undefined, triggersOn: 'click', onShow: function(){}, onClose: function(){}, }; Dropdown.prototype.init = function(){ this.element.classList.add('g-dropdown'); this.element.classList.add('hidden'); this.attachTriggerListener(this.settings.triggersOn, this.trigger, this.element); }; Dropdown.prototype.getElement = function(object){ if(typeof object == 'object' && object instanceof HTMLElement) return object; else if(typeof object == 'string') return document.querySelector(object); }; return Dropdown; }()); var dropdown1 = new Dropdown('#select', { optionSe1lector: 'li', trigger: '#trigger' }); var dropdown2 = new Dropdown('#select2', { optionSe2lector: 'li', trigger: '#trigger2' });
.g-dropdown{ max-height: 100px; max-width: 75px; overflow: scroll; -webkit-transition: visibility 0.50s, height 0.50s; -moz-transition: visibility 0.50s, height 0.50s; transition: visibility 0.50s, height 0.50s; } .g-dropdown.visible{ visibility: visible; height: 100px; } .g-dropdown.hidden{ visibility: hidden; height: 0px; }
<button id='trigger'>Click Me</button> <ul id='select' style='padding: 0; margin: 0;'> <li>One-one</li> <li>Two-two</li> <li>Three-three</li> <li>Four-four</li> <li>Five-five</li> </ul> <br/><br/><br/> <button id='trigger2'>Click Me</button> <ul id='select2' style='padding: 0; margin: 0;'> <li>One-one</li> <li>Two-two</li> <li>Three-three</li> <li>Four-four</li> <li>Five-five</li> </ul>
You could use event.currentTarget
to know which element triggers the event and so find the element you need this target to show, that way your code will shorter and simpler
Try something like this:
var btn1 = document.getElementById('btn-1'); var btn2 = document.getElementById('btn-2'); function toggle(event) { var nextNode = event.currentTarget.nextElementSibling; nextNode.classList.toggle('disable'); nextNode.classList.toggle('enable'); } btn1.addEventListener('click', toggle, false); btn2.addEventListener('click', toggle, false);
.disable { max-height: 0px; overflow: hidden } .enable { max-height: initial; overflow: initial }
<div class="first-container"> <button id="btn-1">Click Me</button> <ul id="list-1" class="disable"> <li>item-1</li> <li>item-2</li> <li>item-3</li> <li>item-4</li> <li>item-5</li> </ul> </div> <div class="second-container"> <button id="btn-2">Click Me</button> <ul id="list-2" class="disable"> <li>item-1</li> <li>item-2</li> <li>item-3</li> <li>item-4</li> <li>item-5</li> </ul> </div>
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.