From 537ec77a0d9be4cd31d0fbac087d5f527e26a675 Mon Sep 17 00:00:00 2001 From: pgarrison Date: Thu, 17 Sep 2020 15:46:15 -0700 Subject: [PATCH] 10 Close other multiselects on open We now use the `:focus-within` selector to handle opening & closing the dropdowns. * `:focus-within` is supported by all major browsers as of January 2020 * The global `hideMultiselects` function has been removed since we aren't using it. This is a breaking change, but it was never publicly documented and I don't see much use case for it anymore. In case someone was using `hideMultiselects`, they can now use `document.activeElement.blur()` instead. --- multiselect.min.js | 45 +------------------------------------ scripts/multiselect.core.js | 39 +++----------------------------- scripts/multiselect.js | 17 -------------- styles/multiselect.css | 14 +++++++++++- 4 files changed, 17 insertions(+), 98 deletions(-) diff --git a/multiselect.min.js b/multiselect.min.js index a3940a4..c5cbaf2 100644 --- a/multiselect.min.js +++ b/multiselect.min.js @@ -1,44 +1 @@ -if(!m_helper) -{var m_helper={removeNode:function(id){var el=document.getElementById(id);if(el){el.parentNode.removeChild(el)}},insertAfter:function(item,target){var parent=target.parentNode;if(target.nextElementSibling){parent.insertBefore(item,target.nextElementSibling)}else{parent.appendChild(item)}},hide:function(element){element.style.display='none'},hideAll:function(array){for(var i=0;i-1){window.multiselects._items.splice(index,1);window.multiselects.splice(index,1)}},select:function(val){this._toggle(val,!0)},deselect:function(val){this._toggle(val,!1)},setIsEnabled(isEnabled){if(this._isEnabled===isEnabled)return;var wrapperItem=document.getElementById(this._getIdentifier());if(isEnabled){wrapperItem.classList.remove('disabled')}else{wrapperItem.classList.add('disabled')} -m_helper.setDisabled(this._item,!isEnabled);m_helper.setDisabled(document.getElementById(this._getInputFieldIdentifier()),!isEnabled);this._isEnabled=isEnabled},_toggle:function(val,setCheck){var self=this;if(val){m_helper.each(document.getElementById(this._getIdentifier()).querySelectorAll('.multiselect-checkbox'),function(e){if(e.dataset.val==val){if(setCheck&&!e.checked){m_helper.check(e);self._onCheckBoxChange(e,self)}else if(!setCheck&&e.checked){m_helper.uncheck(e);self._onCheckBoxChange(e,self)}}});self._updateText(self)}},selectAll:function(val){var selectAllChkBox=document.querySelector('#'+this._getIdentifier()+' .multiselect-checkbox');m_helper.check(selectAllChkBox);this._onCheckBoxChange(selectAllChkBox,this);this._updateText(this)},deselectAll:function(){var selectAllChkBox=document.querySelector('#'+this._getIdentifier()+' .multiselect-checkbox');m_helper.uncheck(selectAllChkBox);this._onCheckBoxChange(selectAllChkBox,this);this._updateText(this)},_checkboxClickEvents:{},setCheckBoxClick(id,handler){if(typeof handler==="function"){this._checkboxClickEvents[id]=handler}else{console.error("Checkbox click handler for checkbox value="+id+" is not a function")} -return this},_appendEvents:function(){var self=this;document.getElementById(self._getInputFieldIdentifier()).addEventListener('focus',function(event){self._showList(self);document.getElementById(self._getInputFieldIdentifier()).value='';m_helper.each(window.multiselects,function(e){if(document.getElementById(e._getItemListIdentifier()).offsetParent&&m_helper.parent(event.target,e._getIdentifier())){e._hideList(self)}})});document.getElementById(self._getInputFieldIdentifier()).addEventListener('click',function(){self._showList(self);document.getElementById(self._getInputFieldIdentifier()).value=''});document.getElementById(self._getIdentifier()).addEventListener('click',function(event){event=event||window.event;var target=event.target||event.srcElement;if(m_helper.parent(target,self._getIdentifier())){event.stopPropagation()}});document.getElementById(self._getItemListIdentifier()).addEventListener('mouseover',function(){self._showList(self)});m_helper.each(document.getElementById(self._getIdentifier()).querySelectorAll('.multiselect-checkbox'),function(e){e.addEventListener('change',function(event){self._onCheckBoxChange(e,self,event)})});var onInput=function(){var text=this.value.toLowerCase();if(!text||text==''){m_helper.show(document.querySelector('#'+self._getItemListIdentifier()+' > span'));m_helper.show(document.querySelector('#'+self._getItemListIdentifier()+' > hr'));m_helper.showAll(document.querySelectorAll('#'+self._getItemListIdentifier()+' li'))}else{m_helper.hide(document.querySelector('#'+self._getItemListIdentifier()+' > span'));m_helper.hide(document.querySelector('#'+self._getItemListIdentifier()+' > hr'));var array=Array.prototype.filter.call(document.querySelectorAll('#'+self._getItemListIdentifier()+' li span'),function(obj){return obj.innerHTML.toLowerCase().indexOf(text)>-1});m_helper.hideAll(document.querySelectorAll('#'+self._getItemListIdentifier()+' li'));m_helper.each(array,function(e){m_helper.show(e.parentElement.parentElement)})}} -document.getElementById(self._getInputFieldIdentifier()).addEventListener('propertychange',onInput);document.getElementById(self._getInputFieldIdentifier()).addEventListener('input',onInput)},_onCheckBoxChange:function(checkbox,self,event){if(!checkbox.dataset.multiselectElement){var checkedState=self._performSelectAll(checkbox,self);if(typeof self._checkboxClickEvents.checkboxAll==="function"){self._checkboxClickEvents.checkboxAll(checkbox,{checked:checkedState})}} -else{var checkedState=self._performSelectItem(checkbox,self);if(typeof self._checkboxClickEvents[checkedState.id]==="function"){self._checkboxClickEvents[checkedState.id](checkbox,checkedState)} -self._updateSelectAll(self)} -self._forceUpdate()},_performSelectItem:function(checkbox,self){var item=JSON.parse(checkbox.dataset.multiselectElement);if(checkbox.checked){self._itemCounter++;m_helper.select(this._item.options[item.index]);m_helper.setActive(checkbox.parentElement.parentElement);return{id:item.id,checked:!0}} -self._itemCounter--;m_helper.deselect(this._item.options[item.index]);m_helper.setUnactive(checkbox.parentElement.parentElement);return{id:item.id,checked:!1}},_performSelectAll:function(checkbox,self){var items=self._getItems();if(checkbox.checked){self._itemCounter=items.length;m_helper.each(items,function(e){m_helper.setActive(e.multiselectElement.parentElement.parentElement);m_helper.select(self._item.options[e.index]);m_helper.check(e.multiselectElement)});return!0} -self._itemCounter=0;m_helper.each(items,function(e){e.multiselectElement.parentElement.parentElement.classList.remove('active');m_helper.deselect(self._item.options[e.index]);m_helper.uncheck(e.multiselectElement)});return!1},_updateSelectAll:function(self){var allChkBox=document.getElementById(self._getItemListIdentifier()).querySelector('input[type=checkbox]');if(self._itemCounter==self._getItems().length){allChkBox.checked=!0} -else if(allChkBox.checked){allChkBox.checked=!1}},_hideList:function(context,event){m_helper.setUnactive(document.getElementById(context._getItemListIdentifier()));m_helper.show(document.getElementById(context._getItemListIdentifier()).querySelector('span'));m_helper.show(document.getElementById(context._getItemListIdentifier()).querySelector('hr'));m_helper.showAll(document.getElementById(context._getItemListIdentifier()).querySelectorAll('li'));context._updateText(context);if(event) -event.stopPropagation()},_updateText:function(context){var activeItems=document.getElementById(context._getItemListIdentifier()).querySelectorAll('ul .active');if(activeItems.length>0){var val='';for(var i=0;i<(activeItems.length<5?activeItems.length:5);i++){val+=activeItems[i].innerText+", "} -val=val.substr(0,val.length-2);if(val.length>20){val=val.substr(0,17)+'...'}} -if(activeItems.length==document.getElementById(context._getItemListIdentifier()).querySelectorAll('ul li').length){val='All selected'} -document.getElementById(context._getInputFieldIdentifier()).value=val?val:''},_showList:function(context){m_helper.setActive(document.getElementById(context._getItemListIdentifier()))},_forceUpdate:function(){var badge=document.getElementById(this._getInputBadgeIdentifier());badge.style.visibility='hidden';if(this._itemCounter!=0){badge.innerHTML=this._itemCounter;badge.style.visibility='visible';var ddArrow=badge.nextElementSibling;if(this._itemCounter<10){badge.style.left='-45px';ddArrow.style.marginLeft='-42px'} -else if(this._itemCounter<100){badge.style.left='-50px';ddArrow.style.marginLeft='-47px'} -else if(this._itemCounter<1000){badge.style.left='-55px';ddArrow.style.marginLeft='-52px'} -else if(this._itemCounter<10000){badge.style.left='-60px';ddArrow.style.marginLeft='-57px'}}},_items:undefined,_itemCounter:0,_isEnabled:!0,_getItems:function(){if(this._items==undefined){var result=[];var opts=this._item.options;for(var i=0;i-1&&(window.multiselects._items.splice(e,1),window.multiselects.splice(e,1))},select:function(e){this._toggle(e,!0)},deselect:function(e){this._toggle(e,!1)},setIsEnabled(e){if(this._isEnabled!==e){var t=document.getElementById(this._getIdentifier());e?t.classList.remove("disabled"):t.classList.add("disabled"),i.setDisabled(this._item,!e),i.setDisabled(document.getElementById(this._getInputFieldIdentifier()),!e),this._isEnabled=e}},_toggle:function(e,t){var n=this;e&&(i.each(document.getElementById(this._getIdentifier()).querySelectorAll(".multiselect-checkbox"),(function(l){l.dataset.val==e&&(t&&!l.checked?(i.check(l),n._onCheckBoxChange(l,n)):!t&&l.checked&&(i.uncheck(l),n._onCheckBoxChange(l,n)))})),n._updateText(n))},selectAll:function(e){var t=document.querySelector("#"+this._getIdentifier()+" .multiselect-checkbox");i.check(t),this._onCheckBoxChange(t,this),this._updateText(this)},deselectAll:function(){var e=document.querySelector("#"+this._getIdentifier()+" .multiselect-checkbox");i.uncheck(e),this._onCheckBoxChange(e,this),this._updateText(this)},_checkboxClickEvents:{},setCheckBoxClick(e,t){return"function"==typeof t?this._checkboxClickEvents[e]=t:console.error("Checkbox click handler for checkbox value="+e+" is not a function"),this},_appendEvents:function(){var e=this;document.getElementById(e._getInputFieldIdentifier()).addEventListener("click",(function(){document.getElementById(e._getInputFieldIdentifier()).value=""})),document.getElementById(e._getIdentifier()).addEventListener("click",(function(t){var n=(t=t||window.event).target||t.srcElement;i.parent(n,e._getIdentifier())&&t.stopPropagation()})),i.each(document.getElementById(e._getIdentifier()).querySelectorAll(".multiselect-checkbox"),(function(t){t.addEventListener("change",(function(i){e._onCheckBoxChange(t,e,i)}))}));var t=function(){var t=this.value.toLowerCase();if(t&&""!=t){i.hide(document.querySelector("#"+e._getItemListIdentifier()+" > span")),i.hide(document.querySelector("#"+e._getItemListIdentifier()+" > hr"));var n=Array.prototype.filter.call(document.querySelectorAll("#"+e._getItemListIdentifier()+" li span"),(function(e){return e.innerHTML.toLowerCase().indexOf(t)>-1}));i.hideAll(document.querySelectorAll("#"+e._getItemListIdentifier()+" li")),i.each(n,(function(e){i.show(e.parentElement.parentElement)}))}else i.show(document.querySelector("#"+e._getItemListIdentifier()+" > span")),i.show(document.querySelector("#"+e._getItemListIdentifier()+" > hr")),i.showAll(document.querySelectorAll("#"+e._getItemListIdentifier()+" li"))};document.getElementById(e._getInputFieldIdentifier()).addEventListener("propertychange",t),document.getElementById(e._getInputFieldIdentifier()).addEventListener("input",t)},_onCheckBoxChange:function(e,t,i){if(e.dataset.multiselectElement){n=t._performSelectItem(e,t);"function"==typeof t._checkboxClickEvents[n.id]&&t._checkboxClickEvents[n.id](e,n),t._updateSelectAll(t)}else{var n=t._performSelectAll(e,t);"function"==typeof t._checkboxClickEvents.checkboxAll&&t._checkboxClickEvents.checkboxAll(e,{checked:n})}t._updateText(t),t._forceUpdate()},_performSelectItem:function(e,t){var n=JSON.parse(e.dataset.multiselectElement);return e.checked?(t._itemCounter++,i.select(this._item.options[n.index]),i.setActive(e.parentElement.parentElement),{id:n.id,checked:!0}):(t._itemCounter--,i.deselect(this._item.options[n.index]),i.setUnactive(e.parentElement.parentElement),{id:n.id,checked:!1})},_performSelectAll:function(e,t){var n=t._getItems();return e.checked?(t._itemCounter=n.length,i.each(n,(function(e){i.setActive(e.multiselectElement.parentElement.parentElement),i.select(t._item.options[e.index]),i.check(e.multiselectElement)})),!0):(t._itemCounter=0,i.each(n,(function(e){e.multiselectElement.parentElement.parentElement.classList.remove("active"),i.deselect(t._item.options[e.index]),i.uncheck(e.multiselectElement)})),!1)},_updateSelectAll:function(e){var t=document.getElementById(e._getItemListIdentifier()).querySelector("input[type=checkbox]");e._itemCounter==e._getItems().length?t.checked=!0:t.checked&&(t.checked=!1)},_updateText:function(e){var t=document.getElementById(e._getItemListIdentifier()).querySelectorAll("ul .active");if(t.length>0){for(var i="",n=0;n<(t.length<5?t.length:5);n++)i+=t[n].innerText+", ";(i=i.substr(0,i.length-2)).length>20&&(i=i.substr(0,17)+"...")}t.length==document.getElementById(e._getItemListIdentifier()).querySelectorAll("ul li").length&&(i="All selected"),document.getElementById(e._getInputFieldIdentifier()).value=i||""},_forceUpdate:function(){var e=document.getElementById(this._getInputBadgeIdentifier());if(e.style.visibility="hidden",0!=this._itemCounter){e.innerHTML=this._itemCounter,e.style.visibility="visible";var t=e.nextElementSibling;this._itemCounter<10?(e.style.left="-45px",t.style.marginLeft="-42px"):this._itemCounter<100?(e.style.left="-50px",t.style.marginLeft="-47px"):this._itemCounter<1e3?(e.style.left="-55px",t.style.marginLeft="-52px"):this._itemCounter<1e4&&(e.style.left="-60px",t.style.marginLeft="-57px")}},_items:void 0,_itemCounter:0,_isEnabled:!0,_getItems:function(){if(null==this._items){for(var e=[],t=this._item.options,i=0;i 0) { @@ -396,10 +367,6 @@ Multiselect.prototype = { document.getElementById(context._getInputFieldIdentifier()).value = val ? val : ''; }, - _showList: function (context) { - m_helper.setActive(document.getElementById(context._getItemListIdentifier())); - }, - //updates counter _forceUpdate: function () { var badge = document.getElementById(this._getInputBadgeIdentifier()); @@ -494,4 +461,4 @@ Multiselect.prototype = { _getInputBadgeIdentifier: function () { return this._getItemUniqueIdentifier() + '_inputCount'; } -} \ No newline at end of file +} diff --git a/scripts/multiselect.js b/scripts/multiselect.js index 1779cee..95bbc38 100644 --- a/scripts/multiselect.js +++ b/scripts/multiselect.js @@ -27,10 +27,6 @@ if (typeof ($) != 'undefined') { return res.length == 1 ? res[0] : $(res); }; - - $(document).click(function (event) { - hideMultiselects(event); - }); } else { document.multiselect = function(selector) { var res = []; @@ -53,17 +49,4 @@ if (typeof ($) != 'undefined') { return res.length == 1 ? res[0] : res; } - - window.onclick = function(event) { - hideMultiselects(event); - }; } - -function hideMultiselects(event) { - m_helper.each(window.multiselects, function(e) { - if (document.getElementById(e._getItemListIdentifier()).offsetParent && - !m_helper.parent(event.target, e._getIdentifier())) { - e._hideList(e, event); - } - }); -} \ No newline at end of file diff --git a/styles/multiselect.css b/styles/multiselect.css index eff8ec4..2bc76bd 100644 --- a/styles/multiselect.css +++ b/styles/multiselect.css @@ -17,6 +17,18 @@ font-weight : 600; } + /* + * Since we gave these labels a tabindex they can potentially get :focus or + * :active, and we don't want that to cause any new styling + */ + .multiselect-wrapper label:focus { + outline: none; + } + + .multiselect-wrapper label:active { + outline: none; + } + .multiselect-wrapper .multiselect-list { z-index: 1; position: absolute; @@ -28,7 +40,7 @@ margin-top: -2px; } - .multiselect-wrapper .multiselect-list.active { + .multiselect-wrapper:focus-within .multiselect-list { display: block; }