MyDropdown

原生js实现简洁的下拉菜单

目前為 2023-04-13 提交的版本,檢視 最新版本

此腳本不應該直接安裝,它是一個供其他腳本使用的函式庫。欲使用本函式庫,請在腳本 metadata 寫上: // @require https://update.gf.qytechs.cn/scripts/463933/1175377/MyDropdown.js

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         MyDropdown
// @namespace    http://https://wish123.cnblogs.com/?MyDropdown
// @version      0.1.1
// @description  原生js实现简洁的下拉菜单
// @author       Wilson

//构造方法
function MyDropdown(options){
    //_count是某实例下的菜单个数
	this._count = 0;
	this._zIndex = 1000;
	this._config = options;
    window._MyDropdownPosIndex = (window._MyDropdownPosIndex||this._zIndex)+1;
    //实例总个数
    window._MyDropdownInsCount = (window._MyDropdownInsCount||0)+1;
    //当前第几个实例
    this.insCount = window._MyDropdownInsCount;
	if(this._config) {
	    this.config(options);
        this.createStyle();
	}
    //返回对象类型
    this.type = function(obj) {
        return Object.prototype.toString
              .call(obj)
              .replace(/^\[object (.+)\]$/, '$1')
              .toLowerCase();
    }
}
MyDropdown.prototype.createStyle = function() {
  if(document.querySelector("#MyDropdownStyle")) {
    return;
  }
  let = style = `
  <style>
.my-dropdown-wrapper {
  display: none;
  position: absolute;
  background-color: #f1f1f1;
  min-width: 160px;
  overflow: auto;
  box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
  z-index: 1;
}

.my-dropdown-wrapper a {
  color: black;
  padding: 12px 16px;
  text-decoration: none;
  display: block;
}

.my-dropdown-wrapper a:hover {background-color: #ddd;}
.my-dropdown-wrapper a.selected {background-color: #ddd;}
.show {display: block;}
.my-dropdown-item-icon svg{width: 24px;height: 24px;float: left;}
.close {
  width: 24px;
  height: 24px;
  display: inline-block;
  line-height: 24px;
  text-align: center;
  font-size: 18px;
  float: right;
}
</style>
  `
  document.body.insertAdjacentHTML("beforeend", style);
}
//设置用户配置
MyDropdown.prototype.config = function(options) {
    var _this = this;
    //设置用户配置
    _this._config = options || _this._config;
    if(_this._config.toggleEvent && _this._config.toggleEvent.toLowerCase() === 'mouseover') {
        _this._config.toggleEvent = 'mouseenter';
    }

    //绑定事件源按钮点击事件
    document.querySelectorAll(_this._config.el||".my-dropdown-btn").forEach(function(item){
        item.addEventListener(_this._config.toggleEvent||'click', function(e){
            _this.create(this);
            _this.show(this);
            e.stopPropagation();
            return false;
        });
    });

    //点击空白,菜单消失
    document.addEventListener('click', function(e){
        if (!e.target.matches(_this._config.el||'.my-dropdown-btn')) {
            _this.hide();
        }
    });
}
//创建菜单
MyDropdown.prototype.create = function(objBtn, options) {
    var _this = this;
    options = options || _this._config;
    if(options.el) {
        _this._config.el = options.el;
    }

    if(objBtn) {
        if (!objBtn.dataset.count) {
            objBtn.dataset.count = _this._count++;
            objBtn.classList.add('my-btn'+_this.insCount+objBtn.dataset.count);
        }
    }
    var count = objBtn.dataset.count || 0;

    //已存在则返回
    if(document.querySelector(`#myDropdownWrapper${_this.insCount}${count}`)) {
        return;
    }
    var mouseenterClass = "";
    if(_this._config.toggleEvent && _this._config.toggleEvent.toLowerCase() === 'mouseenter') {
        mouseenterClass = " mouseenter";
    }
    //生成菜单 这里count是某实例下的菜单个数,_this.insCount是实例个数
    var menu = `<div id="myDropdownWrapper${_this.insCount}${count}" class="my-dropdown-wrapper${mouseenterClass}" style="overflow:auto;${options.maxWidth?'max-width:'+options.maxWidth+';':''}${options.maxHeight?'max-height:'+options.maxHeight+';':''}" data-count="${count}">`;
    if(options.items) {
        for(var i in options.items){
            var item = options.items[i];
            //处理icon选项
            var iconHtml = '';
            if(item.icon){
                if(_this.type(item.icon) === 'object'){
                    iconHtml = item.icon.html ? item.icon.html : '';
                } else {
                    iconHtml = item.icon ? item.icon : '';
                }
            }

            //处理op选项
            var opHtml = '';
            if(item.op){
                if(_this.type(item.op) === 'object'){
                    opHtml = item.op.html ? item.op.html : '';
                } else {
                    opHtml = item.op ? item.op : '';
                }
            }
            //生成菜单项
            menu += `<a href="javascript:;" class="my-dropdown-item ${item.selected?'selected':''}" data-index="${i}" data-value="${item.value}"><span class="my-dropdown-item-icon">${iconHtml}</span>${item.name}<span class="my-dropdown-item-op">${opHtml}</span></a>`;
        }
    }
    menu += `</div>`;
    //追加到body中
    document.body.insertAdjacentHTML("beforeend", menu);

    //绑定菜单事件
    if(_this._config.toggleEvent && _this._config.toggleEvent.toLowerCase() === 'mouseenter') {
        //绑定菜单列表
        document.querySelector("#myDropdownWrapper" + _this.insCount + count).addEventListener("mouseleave", function(e){
            _this.hide(this);
        });
    }

    //绑定菜单item点击事件
    document.querySelectorAll("#myDropdownWrapper" + _this.insCount + count + " .my-dropdown-item").forEach(function(item){
        item.addEventListener("click", function(e){
            if(typeof this.dataset.index !== 'undefined' && options.items[this.dataset.index]) {
                options.items[this.dataset.index].fn(e);
            }
        });
    });

    //绑定菜单项icon按钮点击事件
    document.querySelectorAll("#myDropdownWrapper" + _this.insCount + count + " .my-dropdown-item-icon").forEach(function(item){
        //if(!item.innerHTML) return;
        item.addEventListener("click", function(e){
            if(typeof this.parentElement.dataset.index !== 'undefined' && options.items[this.parentElement.dataset.index] && 
            options.items[this.parentElement.dataset.index].icon && options.items[this.parentElement.dataset.index].icon.fn) {
                var _this_item = this;
                (function(e){
                    options.items[_this_item.parentElement.dataset.index].icon.fn(e);
                    e.stopPropagation();
                    return false;
                })(e);
            }
        });
    });

    //绑定菜单项操作按钮点击事件
    document.querySelectorAll("#myDropdownWrapper" + _this.insCount + count + " .my-dropdown-item-op").forEach(function(item){
        //if(!item.innerHTML) return;
        item.addEventListener("click", function(e){
            if(typeof this.parentElement.dataset.index !== 'undefined' && options.items[this.parentElement.dataset.index] && 
            options.items[this.parentElement.dataset.index].op && options.items[this.parentElement.dataset.index].op.fn) {
                var _this_item = this;
                (function(e){
                    options.items[_this_item.parentElement.dataset.index].op.fn(e);
                    e.stopPropagation();
                    return false;
                })(e);
            }
        });
    });

    if(options.created) options.created(document.querySelector(`#myDropdownWrapper${_this.insCount}${count}`));
};
//菜单显示
MyDropdown.prototype.show = function(objBtn, callback) {
  var count = objBtn.dataset.count || 0;
  var myDropdownWrapper = document.getElementById("myDropdownWrapper"+this.insCount+count);
  if(objBtn){
      myDropdownWrapper.style.top = (objBtn.offsetTop + objBtn.offsetHeight) + "px";
      myDropdownWrapper.style.left = objBtn.offsetLeft + "px";
  }
  if (myDropdownWrapper.classList.contains('show')) {
      if(this._config.toggleEvent && this._config.toggleEvent.toLowerCase() === 'mouseenter'){
        return;
      }
      //隐藏菜单
      myDropdownWrapper.classList.remove('show');
      if(this._config.hidden) this._config.hidden();
  } else {
      if(this._config.toggleEvent && this._config.toggleEvent.toLowerCase() === 'mouseenter'){
        this.hide();
      }
      //显示菜单
      myDropdownWrapper.style.zIndex = window._MyDropdownPosIndex++;
      myDropdownWrapper.classList.add('show');
      callback = callback || this._config.shown;
      if(callback) callback(myDropdownWrapper);
  }
  //myDropdownWrapper.classList.toggle("show");
}
//菜单隐藏
MyDropdown.prototype.hide = function(objMenu, callback) {
    if(objMenu) {
        //单个菜单隐藏
        if (objMenu.classList.contains('show')) {
            objMenu.classList.remove('show');
            callback = callback || this._config.hidden;
            if(callback) callback(objMenu);
        }
    } else {
        var mouseenterClass = '';
        if(this._config.toggleEvent && this._config.toggleEvent.toLowerCase() === 'mouseenter'){
            mouseenterClass = ".mouseenter";
        }
        //所有菜单隐藏
        var dropdowns = document.querySelectorAll(".my-dropdown-wrapper" + mouseenterClass);
        for (var i = 0; i < dropdowns.length; i++) {
          var openDropdown = dropdowns[i];
          if (openDropdown.classList.contains('show')) {
            openDropdown.classList.remove('show');
            callback = callback || this._config.hidden;
            if(callback) callback(openDropdown);
          }
        }
    }
};


//使用示例
//测试1
// var clicked = function(e) {
//     console.log("clicked", e.target.dataset.value)
// }
// new MyDropdown({
//     el: ".my-dropdown-btn",
//     maxWidth: '200px',
//     maxHeight: '400px',
//     //支持click mouseenter dblclick等,默认click
//     toggleEvent: 'mouseenter',
//     items: [
//         {
//             name: 'Home',
//             value: 'home',
//             icon: '',
//             fn: clicked
//         },
//         {
//             name: 'About',
//             value: 'about',
//             icon: '',
//             selected: false,
//             fn: clicked
//         },
//         {
//             name: 'Contact',
//             value: 'contact',
//             icon: '',
//             fn: clicked,
//             //icon也支持对象传值,同样具有html和fn属性
//             op: {
//                 html: `<span class="close">&times;</span>`,
//                 fn: function(e) {
//                     console.log('op clicked');
//                 }
//             }
//         }
//     ],
//     created: function(menu) {
//         console.log('After created callback');
//     },
//     shown: function(menu) {
//         console.log('After shown callback');
//     },
//     hidden: function(menu) {
//         console.log('After hidden callback');
//     }
// });

// //测试2
// var clicked2 = function(e) {
//     console.log("clicked2", e.target.dataset.value)
// }
// new MyDropdown({
//     el: ".my-dropdown-btn2",
//     items: [
//         {
//             name: 'Home2',
//             value: 'home2',
//             icon: '',
//             fn: clicked2
//         },
//         {
//             name: 'About2',
//             value: 'about2',
//             icon: '',
//             selected: false,
//             fn: clicked2
//         },
//         {
//             name: 'Contact2',
//             value: 'contact2',
//             icon: '',
//             fn: clicked2
//         }
//     ],
//     created: function(menu) {
//         console.log('After created callback');
//     },
//     shown: function(menu) {
//         console.log('After shown callback');
//     },
//     hidden: function(menu) {
//         console.log('After hidden callback');
//     }
// });