首页 web前端 js教程 javascript模拟select,jselect的方法实现_javascript技巧

javascript模拟select,jselect的方法实现_javascript技巧

May 16, 2016 pm 05:48 PM
select

由于主流浏览器对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 elem = elems[i]
enabled = elem.getAttribute('data-enabled')
//使用系统select
if(!enabled || enabled === '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 = [],
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 option = options[i]
value = option.value
text = option.innerText || option.textContent

obj = {
value: value,
text: text
}
if(option.selected) {
selectedValue = 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.innerText || elem.textContent,
curValue = 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.innerText || n.textContent
value = n.getAttribute('data-value')
if(curText === text && curValue === 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 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可以通过这里下载

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
2 周前 By 尊渡假赌尊渡假赌尊渡假赌
仓库:如何复兴队友
4 周前 By 尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island冒险:如何获得巨型种子
3 周前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

使用golang进行Select Channels Go并发式编程的异步处理方法 使用golang进行Select Channels Go并发式编程的异步处理方法 Sep 28, 2023 pm 05:27 PM

使用golang进行SelectChannelsGo并发式编程的异步处理方法引言:并发式编程是现代软件开发中的一个重要领域,它可以有效地提高应用程序的性能和响应能力。在Go语言中,使用Channels和Select语句可以简单而高效地实现并发编程。本文将介绍如何使用golang进行SelectChannelsGo并发式编程的异步处理方法,并提供具体的

jquery如何隐藏select元素 jquery如何隐藏select元素 Aug 15, 2023 pm 01:56 PM

jquery隐藏select元素的方法:1、hide()方法,在HTML页面中引入jQuery库,可以使用不同选择器来隐藏select元素,ID选择器将selectId替换为你实际使用的select元素的ID;2、css()方法,使用ID选择器选择需要隐藏的select元素,使用css()方法将display属性设置为none,并将selectId替换为select元素的ID。

jQuery中如何实现select元素的改变事件绑定 jQuery中如何实现select元素的改变事件绑定 Feb 23, 2024 pm 01:12 PM

jQuery是一个流行的JavaScript库,可以用来简化DOM操作、事件处理、动画效果等。在web开发中,经常会遇到需要对select元素进行改变事件绑定的情况。本文将介绍如何使用jQuery实现对select元素改变事件的绑定,并提供具体的代码示例。首先,我们需要使用标签来创建一个包含选项的下拉菜单:

linux要用select的原因是什么 linux要用select的原因是什么 May 19, 2023 pm 03:07 PM

因为select可以使开发者在同时等待多个文件缓冲区,可减少IO等待的时间,能够提高进程的IO效率。select()函数是IO多路复用的函数,允许程序监视多个文件描述符,等待所监视的一个或者多个文件描述符变为“准备好”的状态;所谓的”准备好“状态是指:文件描述符不再是阻塞状态,可以用于某类IO操作了,包括可读,可写,发生异常三种。select是一个计算机函数,位于头文件#include。该函数用于监视文件描述符的变化情况——读写或是异常。1.select函数介绍select函数是IO多路复用的函

mysql的select语法怎么使用 mysql的select语法怎么使用 Jun 01, 2023 pm 07:37 PM

1、SQL语句中的关键词对大小写不敏感,SELECT等效于SELECT,FROM等效于from。2、从users表中选择所有列的,可以用符号*代替列的名称。语法--这是注释--从FEOM指定的[表中],查询出[所有的]数据.*表示[所有列]SELECT*FROM--通过从FROM从指定的[表中],查询出指定列名称(字段)的数据SELECT列名称FROM表名称实例--注意:多个列之间,使用英文的逗号来分隔selectusername,passwordfrom

通过golang实现Select Channels Go并发式编程的性能优化 通过golang实现Select Channels Go并发式编程的性能优化 Sep 27, 2023 pm 01:09 PM

通过golang实现SelectChannelsGo并发式编程的性能优化在Go语言中,使用goroutine和channel实现并发编程是非常常见的。而在处理多个channel的情况下,我们通常会使用select语句来进行多路复用。但是,在大规模并发的情况下,使用select语句可能会导致性能下降。在本文中,我们将介绍一些通过golang实现select

使用golang实现可靠性和鲁棒性的Select Channels Go并发式编程 使用golang实现可靠性和鲁棒性的Select Channels Go并发式编程 Sep 28, 2023 pm 05:37 PM

使用Golang实现可靠性和鲁棒性的SelectChannelsGo并发式编程引言:在现代软件开发中,并发性已经成为了一个非常重要的主题。使用并发编程可以使得程序更具有响应性、更高效地利用计算资源,并且能够更好地处理大规模的并行计算任务。Golang是一种非常强大的并发编程语言,它通过go协程和channel机制,提供了一种简单而有效的方式来实现并发编程

MySQL的select语句如何使用 MySQL的select语句如何使用 May 27, 2023 pm 01:18 PM

select语句可以用回车分隔$sql="select*fromarticlewhereid=1"和$sql="select*fromarticlewhereid=1"都可以得到正确的结果,但有时分开写或许能更明了一点,特别是当sql语句比较长时。批量查询数据可以用in来实现$sql="select*fromarticlewhereid;in(1,3,5)"使用concat连接查询的结果$sql="selectconcat(i

See all articles