在写示例的时候,用到了下拉框,但是原生的下拉框是在是有点难看,然后模仿着写了点,一个是直接在写好的Dom上进行美化,一个是用js生成,然后定义类名及相应的事件来处理1.效果图2.直接是在Dom上美化html文件<div class=“root”> <div id=“selectedItem”> <div id=“promptText”><span id=“spanText”>请选择你喜欢的文字</span><img src="../images/arrowup.png" id=“arrows” /></div> <ul class=“choiceDescription”> <li class=“item”>万水千山,陪你一起看</li> <li class=“item”>万水千山,陪你一起看1</li> <li class=“item”>万水千山,陪你一起看2</li> <li class=“item”>万水千山,陪你一起看3</li> <li class=“item”>万水千山,陪你一起看4</li> </ul> </div></div>css文件ul{ margin: 0; padding: 0; list-style: none;}/* 下拉框包含层 /#selectedItem{ width: 240px; cursor: pointer;}/ 已选中的选项 /#promptText{ position: relative; padding-left: 10px; width: 230px; height: 30px; line-height: 30px; border: 1px solid #d3d3d3; border-radius: 4px; background: #fff; color: #999; font-size: 14px;}/ 图标 /#arrows{ position: absolute; top: 0; right: 0; width: 30px; height: 30px; vertical-align: middle;}#arrows:focus{ outline: none;}/ 下拉可选项包含层 /.choiceDescription{ position: absolute; display: none; /overflow: hidden;/ margin-top: 2px; width: 240px; border: 1px solid #ccc; border-radius: 4px; box-shadow: 0 1px 6px rgba(0, 0, 0, .1); background: #fff;}.show{ display: block;}/ 下拉可选项 /.item{ height: 30px; line-height: 30px; padding-left: 10px; font-size: 15px; color: #666;}.item:hover{ color: #fff; background: rgba(49, 255, 195, 0.67);}js文件(function() { let choiceDescription = document.getElementsByClassName(‘choiceDescription’)[0]; let arrows = document.getElementById(‘arrows’); / 用于判断是否是下拉 / let isDown = false; let selectedItem = document.getElementById(‘selectedItem’); / 对点击下拉进行监听 / selectedItem.addEventListener(‘click’, function() { isDown = !isDown; if(isDown) { / 如果是下拉状态,则显示下拉的选项,并把图标显示为向下的图标 / choiceDescription.className += ’ show’; arrows.src = ‘../images/arrowdown.png’; } else { choiceDescription.className = ‘choiceDescription’; arrows.src = ‘../images/arrowup.png’; } }); choiceDescription.addEventListener(‘click’, function(e) { let promptText = document.getElementById(‘spanText’); let selectElement = e.target; / 判断是否点击的是li标签,防止点击了li标签以外的空白位置 / while(selectElement.tagName !== ‘LI’) { / 如果点中的是当前容器层 / if(selectElement == choiceDescription) { selectElement = null; break; } / 若果不是,则再找父级容器 / selectElement = selectElement.parentNode; } / innerText、innerHTML、value * innerText 是指html标签里的文字信息,单纯的文本,不会有html标签,存在兼容性 * innerHTML 是指包含在html标签里的所有子元素,包括空格、html标签 * value 表单里的元素属性值 * / if(selectElement) { promptText.innerHTML = e.target.innerHTML; } });})()在已有的Dom节点上对Dom绑定事件,我这里的宽度是固定死的,相对来说不是很友好3.js自动生成进行美化html文件<div id=“select” class=“select”></div><script src=“autoGenerateSelect.js”></script><script> (function() { / 当 onload 事件触发时,页面上所有的DOM,样式表,脚本,图片,flash都已经加载完成了 * 当 DOMContentLoaded 事件触发时,仅当DOM加载完成,不包括样式表,图片,flash / document.addEventListener(‘DOMContentLoaded’,function(){ new $Selector({ elementSelector:’#select’, options:[ {name:‘选项1’,value:‘0’}, {name:‘选项2’,value:‘1’}, {name:‘选项3’,value:‘2’} ], defaultText:‘选项2’ }); }) })()</script>css文件html, body, ul{ margin: 0; padding: 0;}ul{ list-style: none;}#select{ padding: 30px 40px 0;}/ 下拉框 /.dropDown{ position: relative; display: inline-block; min-width: 120px; box-sizing: border-box; color: #515a6e; font-size: 14px;}/ 已选中的值包含层 /.selectedOption{ position: relative; box-sizing: border-box; outline: none; user-select: none; cursor: pointer; background: #fff; border-radius: 4px; border: 1px solid #dcdee2; transition: all .2s ease-in-out;}.selectedValue{ display: block; overflow: hidden; height: 28px; line-height: 28px; font-size: 12px; text-overflow: ellipsis; white-space: nowrap; padding-left: 8px; padding-right: 24px;}/ 图标 /.arrowDown{ position: absolute; display: inline-block; top: 50%; right: 8px; margin-top: -7px; font-size: 14px; color: #808695; transition: all .2s ease-in-out; / 字体抗锯齿渲染 / -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;}.arrowDown:before{ content: “”; display: block; width: 6px; height: 6px; background-color: transparent; border-left: 2px solid #808695; border-bottom: 2px solid #808695; transform: rotate(-45deg);}/ 所有选项的包含层 /.optionsContainer{ position: absolute; top: 30px; left: 0; min-width: 120px; max-height: 200px; margin: 5px 0; padding: 5px 0; background: #fff; box-sizing: border-box; border-radius: 4px; box-shadow: 0 1px 6px rgba(0, 0, 0, .2); z-index: 2; transform-origin: center top 0px; transition: all 0.3s; will-change: top, left; transform: scale(1, 0); opacity: 0;}/ 每个选项 /.optionsItem{ line-height: normal; padding: 7px 16px; color: #515a6e; font-size: 12px; white-space: nowrap; cursor: pointer; transition: background .2s ease-in-out;}.itemSelected, .optionsItem:hover{ color: #2d8cf0; background-color: #f3f3f3;}对下拉框初始化/ 私有方法:初始化下拉框 /_initSelector({ / 传入id,class,tag,用于挂载下拉框 / elementSelector = ‘’, / 传入的下拉框选项 / options = [{ name: ‘请选择你喜欢的颜色’, value: ‘0’ }], defaultText = ‘请选择你喜欢的颜色’}) { / 找到要挂载的Dom节点 / this.parentElement = document.querySelector(elementSelector) || document.body; this.options = options; this.defaultText = defaultText; / 下拉框的显示与隐藏状态 / this.downStatus = false; / 下拉框默认选中的值 / this.defaultValue = ‘’; this._createElement();},创建元素节点 / 创建Dom节点 /_createElement() { / 创建下拉框最外层 / let dropDown = document.createElement(‘div’); dropDown.className = ‘dropDown’; / 已选中的选项值 / let selectedOption = document.createElement(‘div’); selectedOption.className = ‘selectedOption’; / 选中的值 / let selectedValue = document.createElement(‘span’); selectedValue.className = ‘selectedValue’; / 先赋值为默认值 / selectedValue.innerText = this.defaultText; / 向下的图标 / let downIcon = document.createElement(‘i’); downIcon.className = ‘arrowDown’; / 将已选中的值的层添加到Dom节点中 / selectedOption.appendChild(selectedValue); selectedOption.appendChild(downIcon); / 创建选项的外层容器 / let optionsContainer = document.createElement(‘div’); optionsContainer.className = ‘optionsContainer’; / 用ul来包含选项层 / let ulOptionsList = document.createElement(‘ul’); ulOptionsList.className = ‘ulOptionsList’; / 循环创建每个选项 / this.options.forEach((item) => { let optionsItem = document.createElement(’li’); / 是否是选中状态 / if(item.name == this.defaultText) { optionsItem.className = ‘optionsItem itemSelected’; } else { optionsItem.className = ‘optionsItem’; } optionsItem.innerText = item.name; ulOptionsList.appendChild(optionsItem); }); / 添加到每个对应的元素里面 / optionsContainer.appendChild(ulOptionsList); dropDown.appendChild(selectedOption); dropDown.appendChild(optionsContainer); this.parentElement.appendChild(dropDown); / 设置Dom元素,挂载、绑定事件 / / 已选中的选项的包含层 / this.selectedOption = selectedOption; / 选中的值 / this.selectedValue = selectedValue; / 下拉框选项包含层 / this.optionsContainer = optionsContainer; this._handleShowOptions(this.parentElement); this._unifyWidth(selectedOption);},显示与隐藏相关事件/ 显示与隐藏事件 /_handleShowOptions(element) { element.addEventListener(‘click’, (e) => { let clickNode = e.target; this._unifyWidth(this.selectedOption); / 点击的是否是下拉框 / if(this._isOptionNode(clickNode, this.selectedOption)) { if(this.downStatus) { this._hiddenDropDown(); } else { this._showDropDown(); } } else if(clickNode.className == ‘optionsItem’) { this._handleSelected(clickNode); } else { this._hiddenDropDown(); } })},/ 判断是否是下拉框选项 /_isOptionNode(clickNode, target) { if (!clickNode || clickNode === document) return false; return clickNode === target ? true : this._isOptionNode(clickNode.parentNode, target);},/ 显示下拉框选项 /_showDropDown() { this.optionsContainer.style.transform = ‘scale(1, 1)’; this.optionsContainer.style.opacity = ‘1’; this.selectedOption.className = ‘selectedOption’; this.downStatus = true;},/ 隐藏下拉框选项 /_hiddenDropDown() { this.optionsContainer.style.transform = ‘scale(1, 0)’; this.optionsContainer.style.opacity = ‘0’; this.selectedOption.className = ‘selectedOption’; this.downStatus = false;},定义点击事件 / 对每个选项的点击事件 /_handleSelected(clickNode) { this.selectedValue.innerText = clickNode.innerText; clickNode.className = ‘optionsItem itemSelected’; this._siblingsDom(clickNode, function(clickNode) { if(clickNode) { clickNode.className = ‘optionsItem’; } }); this._hiddenDropDown();},/ 兄弟节点处理函数 /_siblingsDom(clickNode, callback) { / arguments 是一个对应于传递给函数的参数的类数组对象 * arguments对象是所有(非箭头)函数中都可用的局部变量 * 包含传递给函数的每个参数,第一个参数在索引0处 * arguments对象不是一个 Array,它类似于Array, * 但除了length属性和索引元素之外没有任何Array属性 * / (function (ele) { / arguments.callee * 指向当前执行的函数 * / callback(ele); if (ele && ele.previousSibling) { arguments.callee(ele.previousSibling); } })(clickNode.previousSibling); (function (ele) { callback(ele); if (ele && ele.nextSibling) { arguments.callee(ele.nextSibling); } })(clickNode.nextSibling);},判断宽度/ 判断宽度 /_unifyWidth(selectedOption) { / 找到所有的li标签 / let optionsItem = document.querySelectorAll(’.optionsItem’); let standardWidth = selectedOption.offsetWidth; / 对每个li标签设置宽度 / optionsItem.forEach((item) => { standardWidth = item.offsetWidth > standardWidth ? item.offsetWidth : standardWidth; item.style.width = standardWidth - 32 + ‘px’; selectedOption.style.width = standardWidth + ‘px’; });}(function() { / 定义selector下拉框 / let Selector = function(params) { / 初始化 / this._initSelector(params); }; Selector.prototype = { / 将上面的方法全部放在Selector原型上 / }; / 挂载到window上/ window.$Selector = Selector;})();关于原型与原型链,可以查看我记录的js面试使用js生成的话,相对来说,代码长点,我这里的话,对宽度有做判断,但是不完美关于DOMContentLoaded可以查看这篇文章DOMContentLoaded正在努力学习中,若对你的学习有帮助,留下你的印记呗(点个赞咯^_^)往期好文推荐:判断iOS和Android及PC端纯css实现瀑布流(multi-column多列及flex布局)实现单行及多行文字超出后加省略号微信小程序之购物车和父子组件传值及calc的注意事项