JS学习1:如何写出一个suggest组件
##| suggest是什么?
- suggest(后文统一简称为sug)的中文意思是,’建议,提议;暗示;使想起;启示’。经常被用做用户的输入提示。
- 也就是说sug的主要功能是提示用户输入,而不是一个结果的展示框,更不是一个实时的下拉列表。(为什么要这样区别,后面会娓娓道来~)
- 当然,用sug来做数据的校验更是不合适的。
##| sug的逻辑及会涉及的事件?
好的,我们先来梳理一下当我们使用sug时自己的操作习惯和流程。
- 首先我们期望,sug可以根据自己在输入框中输入或删除字符,而给出不同的提示。也就是说这是一个实时动态变化的。
- 当我们可以根据键盘的上、下键来选择自己本来想要输入的内容,这样自己可以节省很多时间和体力嘛(其实就是懒)
- 当使用键盘选中后,敲回车时,sug中被选中的内容会自动跑到input输入框里。
- 除了使用上下键,我们可能还期望使用鼠标点击某个内容,就可以让被选中的内容跑到输入框里。
- 为了满足第3点,就需要当鼠标移上某个内容时,这个内容必须要与其他未选中的内容区分,否则我们也不知道是选的哪一个啊。
- 另外呢,我们当然还期望有比较好的用户体验啦。
- 点击除了sug列表的其他位置时,sug会消失
- sug列表中默认就选中第一条数据,而不是需要按一下神马键再选中第一条,多此一举。
- 按上、下键来选择内容时,如果列表中只有1条数据,那无论怎么按上下键,就保持原状啊;如果有多条,那可以循环去按呀,从头->尾,从尾->头。
那么按照上述的逻辑,映射到代码层面上,我们会需要监听和涉及到哪些事件呢?
####|| input
- 与ie兼容,同时监听
propertychange
(IE专有,好牛气的赶脚) - 为啥不用onchange事件呢?oninput,onpropertychange,onchange究竟有啥区别呢?
- onpropertychange, IE专有,当前对象的属性(
checked,value,selectedIndex
)发生变化时,都会触发这个事件 - oninput, 现代浏览器可用,只有当前对象的value值发生变化时,才会触发该事件
- onchange, value值发生了变化,并且失去焦点时才会触发;并且是由鼠标或键盘改变,如果是脚本改变的值,那也不能触发该事件。
- 还有一个onkeyup事件,这个能用来监听用户通过键盘输入的value值变化,但是如果是复制粘贴来的值,就无法检测到。
- onpropertychange, IE专有,当前对象的属性(
所以,综上选择oninput & onpropertychange 为监听输入框值变化的方案
####|| click
- 被选中li节点
- 将对应的数据添加到input中
- sug范围外的区域
- remove所有的sug列表
####|| keydown
在这里,值得注意的是,我们需要设置一个sug类的私有变量selectedNode,用来保存当前被选中的节点。这样在之后通过enter或click才能将被选中的值放入input中。
- up, down, enter对应的
e.keycode
分别是38
,40
,13
- up,down
在这儿,需要完成两件事,(1)删除上一次selectedNode的背景,(2)保存当前被选中的节点为selectedNode。
当然,还需要注意,如果sug列表中节点的数量大于1时,才需要完成上面2件事。否则保持现状即可。 - enter
在改写@小生爱 的源码中,有这样一个小bug,如果sug中有值,而sug列表自身被隐藏,enter会把默认选中的第一条选到input中。
为了解决这个问题,在enter时间中,我首先检测当前的sug列表是否是隐藏着的,如果是,那么就不执行enter事件。也就避免了将本来默认选中的第1条数据误添加到input中。
####|| mouseover
鼠标滑过li节点时,对应的节点变为选中状态,增强用户体验。
####|| 确定sug的位置
一般我们期望的sug位置是在input输入框的正下方,所以可以确定:
sug的top = input输入框距当前视口顶部的距离 + input输入框本身的高度
sug的left = input输入框距当前视口左边的距离
- 获得input输入框距当前视口的距离,
$(input).offset()
- input输入框本身的高度,
$(input).offsetHeight
最后,附上sug列表的js代码.
##| 可改进的点
目前这样做完,在体验上还是有可改进点的(目前只想到了这一个- -.等以后想到了随时补充)。
- blur: blur时,检测数据,并remove sug.
##| 具体写码时的坑
placeholder兼容
检测如果文档创建一个新的input,其中是否具有placeholder属性1234567891011121314151617181920212223var placeholder = function (){// 判断是否支持placeholderif('placeholder' in document.createElement('input')) return false;// 要添加holder的表单节点var elInputs = $('.js-placeholder', root);// 初始化表单$.each(elInputs, function (){$(this).val($(this).attr('placeholder'));});// 添加事件elInputs.on('focus blur', function (e){var type = e.type;var elInput = $(this);// 表单默认值var initInput = elInput.attr('placeholder');if(type == 'focus' && elInput.val() == initInput){// 获取焦点 表单置空elInput.val('');}else if(type=='blur' && elInput.val()==''){elInput.val(initInput);}});};纳尼?ie6/7下,背景消失了!!!
这种现象一般是由于代码里同时包含这两句,
display:inline-block;text-indent: -999em
原因:ie6/7并未实现真的的
inline-block
,它只所有具有inline-block
的某些特征,是因为元素触发了hasLayout
,而因此让元素拥有了inline-block
的表症。- 如何解决呢? 无非两种,从
display
来考虑,从与字体相关的考虑 - 方案一:display:block
- 方案二:font-size:0;line-height:0
- 方案三:line-height:900px;overflow:hidden
ie7,
width: 100%
,没宽度了?-
- 不要给input设置固定高度
- 用padding撑开input的高度
- 不要给input设置行高
- 如果需要改变光标的高度,改变font-size的值
如何弥补把sug当作查询结果集的缺点?
- 查询已知的现有数据集,再做一次验证。
pm想用sug当结果显示框,结果呢?
- 之前说到过sug本质上一个提示功能,为用户的输入起到一定的辅助功能。当遇到这样的场景时,就显得特别不合适了。
- 背景:
(1)input中的内容来源有两个,从sug中选中的,用户自己输入的;
(2)当用户输入一个百分百匹配的词时,sug并不会给出提示(你都知道要输入啥了,就没有提示的必要了呀) - 场景:
将用户输入框内的正确内容做为参数传给后端。我们知道sug里的内容,肯定是百分之百正确的,所以根据sug得到的内容可以直接发到后端。但是当用户输入‘望京西’时,这是一个百分百匹配的词,sug中返回的结果是空。
这时,我们如果分辨不是从sug中得来的input内容是正确的还是错误的呢?
也就是说,没有直接的办法可以判断‘望京西’和‘望京西乱七八糟’哪个是正确的。
这时,使用sug作为结果显示框是不妥的,应当像上一条提到的一样,再次查询已知的现有结果集,来做验证。
##| 小技巧
flag && a = b
其实这样等同于,不仅代码变得简洁了,而且更有效率了。123if(flag){a =b}验证是否包含在某个数据集中?Hash,对象
obj['key'] == 1
,不用循环遍历- 指定异步回调函数名123456dataType: jsonp,jsonpCallback: 回调函数名,data:{a : a,b : b}