简体   繁体   中英

Instances conflicting on events

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM