element select 自动展开_js触发select自动展开

Q1:javascript模拟select,jselect的方法实现

由于主流浏览器对select元素渲染不同,所以在每种浏览器下显示也不一样,最主要的是默认情况下UI太粗糙,即使通过css加以美化也不能达到很美观的效果。这对于我们这些专注于UX的前端开发人员是无法容忍的。于是在项目不太忙的时候,就计划写一个模拟的select控件出来。接下来就把实现的细节、遇到的问题以及如何使用和大家分享一下。

1. 实现细节

init: function(context) {

//获取指定上下文所有select元素

var elems = squid.getElementsByTagName(select, context)

this.globalEvent()

this.initView(elems)

}

在一个用户注册的应用场景,有多个select元素。模拟的select控件(以下简称jselect)初始化方法会获取页面上所有select元素,然后绑定全局事件globalEvent,初始化页面显示initView。globalEvent方法如下:

复制代码 代码如下:

globalEvent: function() {

//document 添加click事件,用户处理每个jselect元素展开关闭

var target,

className,

elem,

wrapper,

status,

that = this;

squid.on(document, click, function(event) {

target = event.target,

className = target.className;

switch(className) {

case select-icon:

case select-default unselectable:

elem = target.tagName.toLowerCase() === div ? target : target.previousSibling

wrapper = elem.nextSibling.nextSibling

//firefox 鼠标右键会触发click事件

//鼠标左键点击执行

if(event.button === 0) {

//初始化选中元素

that.initSelected(elem)

if(squid.isHidden(wrapper)) {

status = block

//关闭所有展开jselect

that.closeSelect()

}else{

status = none

}

wrapper.style.display = status

elem.focus()

}else if(event.button === 2){

wrapper.style.display = none

}

that.zIndex(wrapper)

break

case select-option:

case select-option selected:

if(event.button === 0) {

that.fireSelected(target, target.parentNode.parentNode.previousSibling.previousSibling)

wrapper.style.display = none

}

break

default:

while(target && target.nodeType !== 9) {

if(target.nodeType === 1) {

if(target.className === select-wrapper) {

return

}

}

target = target.parentNode

}

that.closeSelect()

break

}

})

}

globalEvent实现了在document绑定click事件,然后在页面上触发点击事件的时候通过事件代理来判断当前点击元素是否是需要进行处理的目标元素,判断条件是通过元素的class,代码中语句的分支分别是:展开当前点击的jselect元素下拉、选中点击列表项、判断是否需要关闭jselect。

initView方法如下:

复制代码 代码如下:

initView: function(elems) {

var i = 0,

elem,

length = elems.length,

enabled;

for(; i < length; i++) {

elem = elems[i]

enabled = elem.getAttribute(data-enabled)

//使用系统select

if(!enabledenabled === true)

continue

if(squid.isVisible(elem))

elem.style.display = none

this.create(elem)

}

}

initView实现了将需要使用jselect替换的select元素先隐藏然后调用create方法,生成单个jselect的整体结构并插入到页面并替代默认select位置。

create方法如下:

复制代码 代码如下:

create: function(elem) {

var data = http://127.0.0.1/yijitao/[],

i = 0,

length,

option,

options,

value,

text,

obj,

lis,

ul,

_default,

icon,

selectedText,

selectedValue,

div,

wrapper,

position,

left,

top,

cssText;

options = elem.getElementsByTagName(option)

length = options.length

for(; i < length; i++) {

option = options[i]

value = http://127.0.0.1/yijitao/option.value

text = option.innerTextoption.textContent

obj = {

value: value,

text: text

}

if(option.selected) {

selectedValue = http://127.0.0.1/yijitao/value

selectedText = text

obj[selected] = true

}

data.push(obj)

}

lis = this.render(this.tmpl, data)

ul =+ lis +

//

div = document.createElement(div)

div.style.display = none

div.className = select-wrapper

//已选元素

_default = document.createElement(div)

_default.className = select-default unselectable

_default.unselectable = on

//让div元素能够获取焦点

_default.setAttribute(tabindex, 1)

_default.setAttribute(data-value, selectedValue)

_default.setAttribute(hidefocus, true)

_default.innerHTML = selectedText

div.appendChild(_default)

//选择icon

icon = document.createElement(span)

icon.className = select-icon

div.appendChild(icon)

//下拉列表

wrapper = document.createElement(div)

wrapper.className = select-list hide

wrapper.innerHTML = ul

//生成新的元素

div.appendChild(wrapper)

//插入到select元素后面

elem.parentNode.insertBefore(div, null)

//获取select元素left top值

//先设置select显示,取完left, top值后重新隐藏

elem.style.display = block

//事件绑定

this.sysEvent(div)

position = squid.position(elem)

elem.style.display = none

left = position.left

top = position.top

cssText = left: + left + px; top: + top + px; display: block;

div.style.cssText = cssText

}

create方法实现了将系统select数据拷贝到jselect下拉列表,jselect的层级关系是最外层有一个class为select-wrapper的元素包裹,里面有class为select-default的元素用于存放已选的元素,class为select-icon的元素用户告诉用户这是一个下拉列表,class为select-list的div元素里面包含了一个ul元素里面是从系统select拷贝的option的文本和值分别存放在li元素的文本和data-value属性。sysEvent方法是为jselect添加点击展开关闭下拉列表事件以及键盘上下选择下拉元素回车选中下拉元素事件。squid.position方法用于获取系统select元素相对于其offsetParent的位置,这里与获取系统select元素的offset是有区别。其实就是获取自己的offset得到top,left值然后分别减去offsetParent获取的offset的top,left值。最后是把jselect插入到系统select元素后面,显示到页面。

jselect创建的基本流程就是上面描述的这样,剩下就是细节地方的实现,比如说:点击展开下拉显示上次已选择的元素,具体实现该功能的是initSelected方法如下

复制代码 代码如下:

initSelected: function(elem) {

var curText = elem.innerTextelem.textContent,

curValue = http://127.0.0.1/yijitao/elem.getAttribute(data-value),

wrapper = elem.nextSibling.nextSibling,

n = wrapper.firstChild.firstChild,

text,

value,

dir,

min = 0,

max,

hidden = false;

for(; n; n = n.nextSibling) {

text = n.innerTextn.textContent

value = http://127.0.0.1/yijitao/n.getAttribute(data-value)

if(curText === text && curValue =http://127.0.0.1/yijitao/== value) {

//显示已选中元素

if(squid.isHidden(wrapper)) {

wrapper.style.display = block

hidden = true

}

max = wrapper.scrollHeight

if(n.offsetTop > (max / 2)) {

if(wrapper.clientHeight + wrapper.scrollTop === max)

dir = up

else

dir = down

}else{

if(wrapper.scrollTop === min)

dir = down

else

dir = up

}

this.inView(n, wrapper, dir)

if(hidden)

wrapper.style.display = none

this.activate(n)

break

}

}

}

该方法接收class为select-default的div元素即用于存放用户已选择内容的元素,具体实现方式是先遍历所有选项获取class有selected的li元素,通过activate方法标示为当前已选中的元素。这里有一个需要计算的地方,就是每次展开下拉列表都要将已选中的元素滚动到页面可视区。因为有可能下来列表内容很多,但是下拉列表的外层select-list会有一个最大的高度,超过最大高度会出现滚动条,默认不做计算的话有可能已选中的元素会在滚动条下面或者是滚动条上面,所以需要通过计算来重置容器滚动条的位置。具体是已选中内容显示到滚动条的上面还是下面需要根据已选中元素的offsetTop值是否大于外层容器select-list的实际高度一半,把已选中元素显示到可视区的方式是inView方法。inView方法如下

复制代码 代码如下:

inView: function(elem, wrapper, dir) {

var scrollTop = wrapper.scrollTop,

//已选中元素offsetTop

offsetTop = elem.offsetTop,

top;

if(dir === up) {

if(offsetTop === 0) {

//滚动条置顶

wrapper.scrollTop = offsetTop;

}else if(offsetTop < scrollTop) {

top = offsetTop - scrollTop

//滚动条滚动到top值

this.scrollInView(wrapper, top)

}

}else{

var clientHeight = wrapper.clientHeight;

if(offsetTop + elem.offsetHeight === wrapper.scrollHeight) {

wrapper.scrollTop = wrapper.scrollHeight - wrapper.clientHeight

}else if(offsetTop + elem.offsetHeight > clientHeight + scrollTop) {

top = (offsetTop + elem.offsetHeight) - (scrollTop + clientHeight)

this.scrollInView(wrapper, top)

}

}

}

inView方法需要判断是向上滚动还是向下滚动,scrollInView方法代码很简单就是把下拉列表外层容器的scrollTop设置为指定的值。方法实现如下

复制代码 代码如下:

scrollInView: function(elem, top) {

setTimeout(function() {

elem.scrollTop += top

}, 10)

} 这个方法实现放到了setTimeout里面做了一个延迟添加到javascript执行队列里面,主要解决的是IE8下展开下拉列表滚动条会最终滚动到顶部,忽略代码设置的scrollTop(从表现上来看好像对scrollTop的设置也能生效,但是最后会重置滚动条到顶部,不知道IE8为什么会有这个问题。),不能把已选中的元素显示到可视区范围,其他浏览器下不会有这个问题。

整个的实现细节大致就这么多,键盘上下键回车键,关闭下拉列表逻辑都很简单。

遇到的问题

如何让div获取焦点来响应键盘keydown, keyup, keypress事件,到谷歌(非常时期谷歌都不好用了,没办法谁让这是咱的特色呢)查找一些资料最后发现需要为div元素设置tabindex属性,这样就可以让div元素获取焦点,来响应用户的操作。因为浏览器在默认情况下双击或者是点击太频繁的话会选中当前区域,为了取消这个默认操作给用户一个好的体验需要为div元素添加一个属性unselectable,不过这个属性只能适用于IE浏览器,其他浏览器下可以通过添加一个class名字是unselectable来避免这个问题。其他的问题都是逻辑上的控制,还有一些位置的计算了,这里就不再说了。

使用方法

首先是在页面模板把希望通过jselect替换的元素隐藏或者不做任何处理,默认情况下jselect会获取页面所有select依次替换,如果不希望jselect替换的select元素

需要添加自定义属性data-enabled="true"。当然添加data-enabled="false"和没有这个自定义属性一样都会被jselect替换。在使用的过程中可能对于布局结构比较复杂的页面还会有其他的问题,因为我测试的页面结构很简单,所以可能没有测试出来。

使用jselect需要先引入squid.js,然后引入jselect-1.0.js, jselect-1.0.css文件,在需要调用jselect的地方通过如下的调用方式来初始化jselect:squid.swing.jselect();

注:jselect源码以及demo可以通过这里下载。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/409966.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[html] 举例说明html的修饰元素有哪些?

[html] 举例说明html的修饰元素有哪些&#xff1f; 加粗&#xff1a;strong、b 倾斜&#xff1a;i、em 下划线&#xff1a;ins 删除线&#xff1a;del 自带样式&#xff1a;p、ul、ol、li、table、tr、td、thead、tbody等个人简介 我是歌谣&#xff0c;欢迎和大家一起交流前后…

vscode python环境_在vscode中配置python环境

原博文 2019-09-27 22:55 − 1.安装vscode和python3.7&#xff08;安装路径在&#xff1a;E:\Python\Python37&#xff09;&#xff1b; 2.打开vscode&#xff0c;在左下角点击设置图标选择setting&#xff0c;搜索python path&#xff0c;在该路径下选择python的安装路径&…

[转] 【领导必读】唐僧为什么可以领导孙悟空

小时候读西游记总有一个疑问——那个唐僧那么无能&#xff0c;为什么孙悟空非要带着他去取经呢&#xff1f;如果孙悟空自己去取经&#xff0c;不就麻烦少多了么&#xff1f;后来长大了&#xff0c;工作了&#xff0c;先被人领导&#xff0c;之后又领导别人&#xff0c;总算明白…

python plt.show_如何使用Python最大化plt.show()窗口

因爲我在零信譽我不會留下任何其他的標誌而不是新的答案 我在Windows(WIN7)上運行Python 2.7.5 & Matplotlib 1.3。1我能夠使用以下行以最大化TkAgg&#xff0c;QT4Agg和wxAgg圖窗口&#xff1a;from matplotlib import pyplot as plt### for TkAgg backendplt.figure(1)pl…

ufw防火墙规则不生效

正式站系统是Ubuntu 16.04.6 一、今天一个项目有百度爬出&#xff0c;在nginx中封掉还在一直爬取&#xff0c;都403还不停爬取 二、在uwf封掉爬出ip&#xff0c;想封掉80端口没有用&#xff0c;然后封掉整个网段还是没有用&#xff0c;尴尬 三、放出终极大招 UFW(iptables)规则…

[html] html的标签元素分为哪几大类?分别有什么作用?

[html] html的标签元素分为哪几大类&#xff1f;分别有什么作用&#xff1f; 行内&#xff0c;块&#xff0c;行内块&#xff1b;单&#xff0c;双&#xff1b;个人简介 我是歌谣&#xff0c;欢迎和大家一起交流前后端知识。放弃很容易&#xff0c; 但坚持一定很酷。欢迎大家…

network怎么断点调试_Windows 网络编程:调试 API

一次性进群&#xff0c;长期免费索取教程&#xff0c;没有付费教程。教程列表见微信公众号底部菜单进微信群回复公众号&#xff1a;微信群&#xff1b;QQ群&#xff1a;460500587微信公众号&#xff1a;计算机与网络安全ID&#xff1a;Computer-network在Windows中有这么一些AP…

uva minesweep 水题

格式控制那块&#xff0c;用cin输入&#xff0c;输出的时候有问题&#xff0c;但不知道为什么能过 不知道是不是我理解错了 #include <cstdio> #include <iostream> using namespace std;char a[102][102];int n,m;int add(int q,int p) {int mines0;if(a[q][p]*)r…

nodejs,express链式反应

链式反应--next()const myexpress require(express); const bodyparser require(body-parser); var server myexpress(); server.listen(8080); server.use(/,function (req,res,next) {console.log(a);req.name "陈培昌";next() });server.use(/,function (req,…

[html] 列举下哪些块元素里面不能放哪些块元素呢?

[html] 列举下哪些块元素里面不能放哪些块元素呢&#xff1f; p、h1、...、h6等不能嵌套块级元素。 ul、ol、table、dl等内部必须是固定的元素。个人简介 我是歌谣&#xff0c;欢迎和大家一起交流前后端知识。放弃很容易&#xff0c; 但坚持一定很酷。欢迎大家一起讨论 主目…

债券价格和到期收益率的关系_[债券知识]什么是到期收益率?到期收益率与债券价格的关系 - 南方财富网...

什么是到期收益率&#xff1f;到期收益率与债券价格的关系在投资市场上&#xff0c;有许多的专业术语需要投资者进行学习&#xff0c;对于债券基本知识&#xff0c;也需要我们去了解&#xff0c;比如什么是到期收益率&#xff1f;债券价格与到期收益率有什么关系&#xff1f;以…

非会员只能试看20分钟_做欧包都要把铁锅烤半小时?太费电了,用我这办法只需预热几分钟...

最近迷上了做欧包&#xff0c;做欧包要用蒸烤箱&#xff0c;做好的欧包才会外脆内软&#xff0c;如果没有蒸烤箱的话人们又研究出了用铸铁锅来做&#xff0c;先把铸铁锅放在烤箱里烘烤半小时左右&#xff0c;然后把面包坯子放入铸铁锅盖好盖子迅速放回烤箱&#xff0c; 因为锅的…

顺序表归并

对两个顺序表进行合并。 思想&#xff1a;定义两个变量&#xff08;i&#xff0c;j&#xff09;分别指向顺序表 A和B当前处理的元素&#xff0c;若i元素不大于j元素&#xff08;<&#xff09;&#xff0c;则把i元素复制到新表中&#xff0c;否者将j元素复制到新表中。 View …

vue下拉框值改变事件_vue和element ui 下拉框select的change事件

1 在线编辑测试工具编辑好代码后点击 run 即可2 vue原生写法2.1 html部分新增空{{ drug.name }}刪除尚未新增任何資料總金額&#xff1a;{{ totalMoney }}{{$data|json}}2.2 js部分new Vue({el: #app,data() {return {selected: null,items: [],// 按照系統正常流程&#xff0c…

Redis的常用命令及数据类型

Redis支持的五种数据类型 字符串 (string)字符串列表 (list)散列 (hash)字符串集合 (set)有序字符串集合 (sorted-set)key&#xff08;键&#xff09; keys * 获取所有的key select 0 选择第一个库 move myString 1 将当前的数据库key移动到某个数据库,目标库有&#xff0c;则不…

collection集合 多少钱_Java集合框架大汇总,建议收藏

Java集合Java集合框架&#xff1a;是一种工具类&#xff0c;就像是一个容器可以存储任意数量的具有共同属性的对象。Java集合中成员很丰富&#xff0c;常用的集合有ArrayList&#xff0c;HashMap&#xff0c;HashSet等。线程安全的有Vector&#xff0c;HashTable。线程不安全的…

项目管理思考——我适合做项目经理吗

一般来说项目经理这个职位是从事IT工作的诸位兄弟姐妹的奋斗目标。而IT行业的特点决定了外行很难领导内行&#xff0c;因此这个职位上的人员都是经历过一线开发的人员一步一个脚印升上去的&#xff0c;而很少有空降一个外行来担当的情况发生。 不同的公司职位设置不同&#xff…

[html] IE6文字溢出BUG(别名:多出来的猪、谍影重重)怎么解决呢?

[html] IE6文字溢出BUG&#xff08;别名&#xff1a;多出来的猪、谍影重重&#xff09;怎么解决呢&#xff1f; 删除注释或不设置浮动个人简介 我是歌谣&#xff0c;欢迎和大家一起交流前后端知识。放弃很容易&#xff0c; 但坚持一定很酷。欢迎大家一起讨论 主目录 与歌谣…

vueinput里只能输入数字_大佬们,vue里面 限制input只能输入数字怎么做啊?

可以参考下面的&#xff0c;目前这个是限制&#xff0c;只能输入数字&#xff0c;小数点只保留2位&#xff0c;限制不能输入空格new Vue({el: "#app",data: {a: ""},methods: {change(val) {val val.replace(/(^\s*)|(\s*$)/g, "")if(!val) {th…