重写select
select 模拟
目前仿写select的方式
- 给
tableIndex来使div(无法获取焦点的元素)来获取元素,这样便可以在失去焦点时,是否将下拉框收回 - 通过
document的点击,来判断是否点击了当前元素 - 利用
input的自带click/blur来处理
ui
看了这么多实现方式,我更好奇具体有什么实现方式
| ui | 实现方式 |
|---|---|
| heyui | document -- click/contextmenu |
| iview | tableIndex/document |
| element-ui | document -- mouse |
| fish-ui | document -- click |
| radon-ui | window -- click |
| mdui | document -- click |
开始仿写
要求
- 只实现单选
- 用原生实现,不基于框架
- 没有使用上述中将下拉框独立出来
- 只做向下下拉,没有高度不够时,可以向上或向下
html
<div class="sc-select-content" data-toggle="false">
<label for="" class="sc-select--label">下拉框</label>
<div class="sc-input--item">
<input id="input" type="text" class="sc-input" readonly autocomplete="off" placeholder="请选择">
<i class="sc-select-icon"></i>
</div>
<div class="sc-select-item">
<ul class="select-container">
<li class="select-item select-item--1">1</li>
<li class="select-item">2</li>
<li class="select-item">3</li>
<li class="select-item">4</li>
<li class="select-item">5</li>
</ul>
<div class="arrow"></div>
</div>
</div>
css
* {
box-sizing: border-box;
}
*::before,
*::after {
box-sizing: border-box;
}
.sc-select-content {
position: relative;
display: inline-block;
width: 200px;
}
.sc-input {
width: 100%;
height: 38px;
outline: none;
padding: 0 15px;
border-radius: 4px;
transition: border-color .2s cubic-bezier(.645, .045, .355, 1);
border: 1px solid #ccc;
padding-right: 34px;
}
.sc-input--item {
position: relative;
}
.sc-select-icon {
position: absolute;
right: 8px;
top: 16px;
box-sizing: content-box;
border-right: 10px solid transparent;
border-left: 10px solid transparent;
border-top: 10px solid #ccc;
}
.sc-select-icon::after {
content: '';
position: absolute;
right: -10px;
bottom: 2px;
box-sizing: content-box;
border-right: 10px solid transparent;
border-left: 10px solid transparent;
border-top: 10px solid #fff;
}
.sc-input:focus {
border: 1px solid #FF9310;
/* box-shadow: 0 0 0 2px rgba(255, 197, 37, 1); */
}
.sc-select-item {
width: 100%;
display: none;
}
.sc-select-item.active {
display: block;
visibility: hidden;
opacity: 0;
transform: translateY(10px);
transition: all .3s cubic-bezier(.55,0,.1,1);
position: absolute;
padding-top: 10px;
}
.sc-select-item.active2 {
visibility: visible;
opacity: 1;
transform: translateY(0);
}
.select-container {
position: relative;
padding: 6px 0;
margin: 0;
display: flex;
flex-direction: column;
border-radius: 4px;
border: 1px solid #e4e7ed;
box-shadow: 0 5px 10px rgba(0, 0, 0, .1);
}
.sc-select-item .arrow {
position: absolute;
width: 0;
height: 0;
border-color: transparent;
border-style: solid;
border-width: 6px;
filter: drop-shadow(0 2px 12px rgba(0,0,0,.03));
top: 6px;
left: 20px;
border-bottom-color: #ebeef5;
border-top-width: 0;
}
.sc-select-item .arrow::after {
position: absolute;
display: block;
width: 0;
height: 0;
border-color: transparent;
border-style: solid;
content: " ";
border-width: 6px;
top: 1px;
margin-left: -6px;
border-bottom-color: #fff;
border-top-width: 0;
}
.select-item {
padding: 0 15px;
height: 38px;
line-height: 38px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
cursor: pointer;
}
.select-item:hover {
background-color: #ddd;
}
*注意 **:
js
/**
* @description: 找到一个符合的parent
* @param {Note} node 元素
* @param {string} className 元素class类名
* @return: Node
*/
function findParent(node, className) {
let parent = node.parentNode
while (parent && parent.classList && [].slice.call(parent.classList).indexOf(className) < 0) {
parent = parent.parentNode
}
if (parent && parent.classList && [].slice.call(parent.classList).indexOf(className) > -1) {
return parent
}
}
const Input = document.getElementById('input')
Input.addEventListener('click', function (e) {
const P = findParent(this, 'sc-select-content')
const Select = findChild(P, 'sc-select-item')
const toggle = P.dataset.toggle
if (toggle === 'true') {
Select.classList.remove('active2')
setTimeout(function () {
Select.classList.remove('active')
P.dataset.toggle = false
}, 100)
} else {
console.log(22)
Select.classList.add('active')
setTimeout(function () {
Select.classList.add('active2')
P.dataset.toggle = true
}, 16)
}
}, false)
Input.addEventListener('blur', function (e) {
const that = this
const P = findParent(this, 'sc-select-content')
const Select = findChild(P, 'sc-select-item')
document.addEventListener('click', function (e) {
const isP = findParent(e.target, 'sc-select-content') === P
if (!isP) {
Select.classList.remove('active2')
setTimeout(function () {
Select.classList.remove('active')
// 时间需要与 transition的时间相同为好
P.dataset.toggle = false
}, 100)
}
}, false)
Select.addEventListener('click', function (e) {
if (e.target.tagName.toLowerCase() === 'li') {
that.value = e.target.innerText
}
Select.classList.remove('active2')
setTimeout(function () {
Select.classList.remove('active')
// 时间需要与 transition的时间相同为好
// toggle = false
P.dataset.toggle = false
}, 100)
}, false)
}, false)
上述方式
- 虽然很low,很多方法可以提出来,偷个懒,先如此写
- 点击,打开关闭
- 主要利用input的
focus和blur方法 - document事件放在里面,是为了拿到上面点击的元素
- 使用
data来存储是否打开还是关闭的boolean

总结
- 虽然实现的很粗糙,但是更多的是为了了解其他ui是如何实现的
- 有机会再细细优化了
重写select的更多相关文章
- 重写select样式
select {/*Chrome和Firefox里面的边框是不一样的,所以复写了一下*/border: solid 1px #000; /*很关键:将默认的select选择框样式清除*/appeara ...
- 自定义属性的时候,尽量不要使用value这个命名
最近我在重写select下拉组件时,使用ul->li来模拟select中的一个个option,并给li添加索引,取名为value. 非IE浏览器下value值工作正常,但是IE下value值工作 ...
- 关于TCP连接建立与终止那点事
0. 前言 最近在处理公司遗留项目的时候发现自己对TCP协议一点都不懂,所以补了点关于TCP连接的建立和终止的内容,这里简单写下自己了解的部分,省略了报文序号确认序号这些无关的字段,主要讨论TCP状态 ...
- ASP.NET Aries 高级开发教程:使用存储过程(番外篇)
前言: 发现这个问题,有不少人提起过,所以就简单写成文章吧. 接下来看如何在Aries 框架中使用存储过程,整体步骤和绑定普通视图差不多. 步骤一:新建一个空视图. 可以在SqlCode管理中,创建一 ...
- 3.2 Zend_Db_Select
10.4. Zend_Db_Select 你能够使用该对象和它的对应方法构建一个select查询语句,然后生成 字符串符用来传送给zend_db_adapter进行查询或者读取结果. 你也能够在你的查 ...
- RocketMQ学习笔记(9)----RocketMQ的Producer 顺序消息
1. 顺序消息原理图 2. 什么是顺序消息? 消费消息的顺序要求同发送消息的顺序一致,在RocketMQ中,主要指的是局部顺序,即一类消息为满足顺序性,必须Producer单线程顺序发送,并且发送给到 ...
- 【转帖】从 Oracle 到 PostgreSQL ,某保险公司迁移实践 技术实践
从 Oracle 到 PostgreSQL ,某保险公司迁移实践 http://www.itpub.net/2019/11/08/4108/ 信泰人寿保险股份有限公司 摘要:去O一直是金融保险行业永恒 ...
- 【Oracle】SQL/92 执行多个表的连接
内连接 外连接 自连接 交叉连接 1.内连接 表名 INNER JOIN 表名 ON 条件 等价于: FROM 表名, 表名 WHERE 条件 SELECT p.name, pt.name, pt.p ...
- Rocket Mq 常用API 及简单运维
RocketMQ 常用API 消息 消息消费模式 消息消费模式由消费者来决定,可以由消费者设置MessageModel来决定消息模式. 消息模式默认为集群消费模式 consumer.setMessag ...
随机推荐
- luogu 4059 [Code+#1]找爸爸 动态规划
Description 小A最近一直在找自己的爸爸,用什么办法呢,就是DNA比对.小A有一套自己的DNA序列比较方法,其最终目标是最 大化两个DNA序列的相似程度,具体步骤如下:1.给出两个DNA序列 ...
- Codeforces Round #345 (Div. 2) E. Table Compression 并查集+智商题
E. Table Compression time limit per test 4 seconds memory limit per test 256 megabytes input standar ...
- Java虚拟机之JVM系统和内存模型
1.类加载子系统 负责从文件系统或者网络中加载Class信息,加载的信息存放在一块称之为方法区的内存空间里. 2.方法区 存放类信息.常量信息.常量池信息.包括字符串字面量和数字常量等,方法区的大小决 ...
- jenkins 管理员密码重置
jenkins管理员 admin的密码忘记怎么重置呢? 修改admin的加密密码为123456的加密密码 #jbcrypt:$2a$10$MiIVR0rr/UhQBqT.bBq0QehTiQVqgNp ...
- vue-router(转)——基本使用 + 路由守卫无限循环问题
路由守卫无限循环问题 https://www.jianshu.com/p/1187f8f74a72 学习目的 学习Vue的必备技能,必须 熟练使用 Vue-router,能够在实际项目中运用. Vue ...
- 搭建Ambari 2.6.0 tar 解压缩报错
背景:我们使用的方式不是wget 去下载ambari的源码包,而是在windows 的 firefox 下直接下载,将文件存储到本地. 执行 tar -zxvf HDP-2.6.3.0-centos7 ...
- redis的安装及使用总结
Windows版本的安装 下载地址:https://www.jb51.net/softs/541181.html 安装过程 把压缩包内的文件解压到非中文目录即可 启动redis 启动redis要通过命 ...
- win10笔记本设置管理员权限
1.在右下方任务栏的“搜索web和windows”输入框中输入“gpedit.msc”,电脑会自行搜索,搜索完毕之后鼠标点击打开.
- Linux shell - cut命令用法(转载)
cut [-bn] [file] 或 cut [-c] [file] 或 cut [-df] [file] 使用说明 cut 命令从文件的每一行剪切字节.字符和字段并将这些字节.字符和字段写至标 ...
- libusb获取usb设备的idVendor(vid),idProduct(pid),以及Serial Number
发表于2015/6/23 21:55:11 4594人阅读 最近在做关于usb设备的项目,用到了libusb,发现关于这个的函数库的介绍,讲解很少,下面仅仅是简单展示一些基本的使用方法,以备后用. ...