MyDropdown

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

Pada tanggal 13 April 2023. Lihat %(latest_version_link).

Skrip ini tidak untuk dipasang secara langsung. Ini adalah pustaka skrip lain untuk disertakan dengan direktif meta // @require https://update.greasyfork.org/scripts/463933/1175377/MyDropdown.js

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

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

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

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

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

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==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');
//     }
// });