之前做了一个网页,网页中的所有输入框都被设计为“获得鼠标焦点时外边框不变蓝”。突然某一天,产品觉得这样用户体验不好,不能很明显地告诉用户当前鼠标停在哪里,于是要求改为“当输入框获得鼠标焦点时,外边框自动变蓝;失去焦点时外边框恢复原样”。
浏览器自动会给输入框 input
、 textarea
的 outline
属性设置一个默认值,效果就是当输入框获得鼠标焦点时外边框会自动带上颜色,失去焦点时外边框颜色消失。由于不同浏览器 outline
属性的默认值不同,导致外边框的颜色也不同。如果不想要浏览器的 outline
默认设置,只需要将 outline
属性设置为 none
即可。
所以一开始听到这个需求,感觉特别简单,不就是纯 CSS 的问题嘛!我只要将所有输入框的 outline
属性设置为浏览器默认的值就好了,但我想的太天真了。。。。
由于网页中已有的输入框的样式遍布在各个文件中,有一种非常机械的办法,那就是到每个文件中去删除掉输入框的 outline:none
样式,这样输入框就会使用浏览器默认的 outline 样式了。但是这样做要改的地方太多了,会疯掉的!再想想有没有更省事的方法,有了!在最基本的 base.css
样式文件中给输入框添加一个 focus
时候的 outline
样式不就解决了么。那么问题来了,该将 outline
设置何值,才能自动使用浏览器默认的 outline
样式呢?
我第一个想到的方法是将 outline
设置为 auto
,但发现只有webkit 内核的浏览器才支持这个属性值,firefox 不支持,因为 auto
并不是标准的 outline
值。所以要想直接复制浏览器默认的 outline
样式是做不到的。难道我要针对每个浏览器设置不同的 outline
值,为了这么一点破需求费那么大工夫?!算了,省事点的方式还是直接摒弃浏览器的默认 outline
样式,统一将 outline
设置为一种样式好了。
但我发现了新问题,通过设置 outline
样式为输入框添加外边框,效果并不好。因为 outline
没有类似 border-radius
的属性来改变边角的弧度。这样就导致了一个问题:当输入框设置了 border
颜色,同时 border-radius
为 3px
以上时,能明显的看到 outline
外边框并没有和 border
重合。看来设置 outline
来达到边框变蓝效果并不是最好的选择,为什么不选用设置 border
来达到同样的效果呢?只要将 border
再设置下 border-shadow
,那么看起来也是和 outline
效果一样的,而且 border
还能设置 border-radius
,显示效果更满足需求。
通过设置 border 解决了大多数输入框,但发现又有了新问题。
网页中有些输入框其实并不是单一使用 input
/ textarea
来实现的,这类“输入框”看起来和一般输入框并无两样,但其实 HTML 结构是一个 div
里面包含着一个 input
/ textarea
来实现的。而且输入框的边框设置在了父元素 div
上,所有当输入框获得焦点时,看到的应该是父元素 div
上的边框变蓝,而不是里面的 input
/ textarea
的边框变蓝。
要想获得焦点时父元素 div
的边框变蓝,肯定可行的方法是通过 JS 来实现:给 input
/ textarea
绑定一个 focus
和 一个 blur
事件,在 focus
事件处理函数中将父元素 div
的边框置蓝,在 blur
事件处理函数中将父元素 div
的边框恢复原样。这种方法时绝对可行的,但是我总觉得应该有更简单的,纯粹使用 css 实现的方法。
如果想单纯通过css 实现,首先想到的是用选择器 div:focus
。但是发现 div
上无法使用 :focus
伪类。于是我就猜想:可能并不是所有的元素都是 focusable的,那么得找一份说明哪些元素 focusable的规范。
搜索结果:
根据 DOM Level 2 HTML规范,focusable 的DOM元素都会有一个原生的 focus()
方法,只有 focusable 的DOM 元素才有 focus 事件,才能使用 :focus
伪类。拥有原生的 focus()
方法的DOM元素包括几种:HTMLInputElement, HTMLSelectElement, HTMLTextAreaElement and HTMLAnchorElement。很明显,规范中遗漏了HTMLButtonElement and HTMLAreaElement。
虽然规范这么定义,但浏览器在实现时却是另外一套。浏览器给任何一个 HTMLElement 都定义 focus()
方法,但并不是任何一个HTMLElement 都能获得焦点(获得焦点术语叫 active, 具体请参考: http://help.dottoro.com/ljqmdirr.php )。一般来说,任何一个时刻HTML 文档中只会有一个active元素,但并不是任何一个元素都能成为active元素。能成为active 的元素包括:
表单元素(form controllers): input/option/textarea/button
链接元素(links): a
标签、 area
标签(必须要带 href
属性,包括 href
属性为空)
可以被编辑的元素(包括通过添加 contenteditable = "true"
属性变成可编辑的情况)
设置了 tabindex
属性( tabindex
值非-1)的元素
window
:当页面窗口从隐藏变成前置可见时, focus
事件就会触发
根据搜索结果,要想将父元素 div
变成focusable,只需要在元素上设置 tabindex 属性,然后通过 :focus
伪类设置父元素 div
获得焦点时的 border
样式。但我发现当鼠标点击里面的 input
元素时,父元素 div
并没有获得焦点,获得焦点的是子元素 input
。
所以最后还是没能通过纯 css 的方法来解决这个问题,无奈之下我还是通过js去解决了。。。。。。