PlaceHolder的两种实现方式

placeholder属性是HTML5 中为input添加的。在input上提供一个占位符,文字形式展示输入字段预期值的提示信息(hint),该字段会在输入为空时显示。


1

<input type="text" name="loginName" placeholder="邮箱/手机号/QQ号">

目前浏览器的支持情况

浏览器 IE6/7/8/9 IE10+ Firefox Chrome Safari 
是否支持 NO YES YES YES YES

然而,虽然IE10+支持placeholder属性,它的表现与其它浏览器也不一致

  • IE10+里鼠标点击时(获取焦点)placeholder文本消失
  • Firefox/Chrome/Safari点击不消失,而是键盘输入时文本消失

这相当恶心,如果使用了placeholder属性。产品经理还是不依不饶,会讲为什么IE里是点击的时候提示文本消失,Chrome里却是键盘输入的时候提示文本消失。要求前端工程师改成一样的表现形式。鉴于此,以下两种实现方式均不采用原生的placeholder属性。

两种方式的思路

  1. (方式一)使用input的value作为显示文本
  2. (方式二)不使用value,添加一个额外的标签(span)到body里然后绝对定位覆盖到input上面

两种方式各有优缺点,方式一占用了input的value属性,表单提交时需要额外做一些判断工作,方式二则使用了额外的标签。

方式一


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

/**

 * PlaceHolder组件

 * $(input).placeholder({

 *   word:     // @string 提示文本

 *   color:    // @string 文本颜色

 *   evtType:  // @string focus|keydown 触发placeholder的事件类型

 * })

 *

 * NOTE:

 *   evtType默认是focus,即鼠标点击到输入域时默认文本消失,keydown则模拟HTML5 placeholder属性在Firefox/Chrome里的特征,光标定位到输入域后键盘输入时默认文本才消失。

 *   此外,对于HTML5 placeholder属性,IE10+和Firefox/Chrome/Safari的表现形式也不一致,因此内部实现不采用原生placeholder属性

 */

$.fn.placeholder = function(option, callback) {

    var settings = $.extend({

        word: ‘‘,

        color: ‘#ccc‘,

        evtType: ‘focus‘

    }, option)

    function bootstrap($that) {

        // some alias

        var word    = settings.word

        var color   = settings.color

        var evtType = settings.evtType

        // default

        var defColor = $that.css(‘color‘)

        var defVal   = $that.val()

        if (defVal == ‘‘ || defVal == word) {

            $that.css({color: color}).val(word)

        } else {

            $that.css({color: defColor})

        }

        function switchStatus(isDef) {

            if (isDef) {

                $that.val(‘‘).css({color: defColor})   

            } else {

                $that.val(word).css({color: color})

            }

        }

        function asFocus() {

            $that.bind(evtType, function() {

                var txt = $that.val()

                if (txt == word) {

                    switchStatus(true)

                }

            }).bind(‘blur‘, function() {

                var txt = $that.val()

                if (txt == ‘‘) {

                    switchStatus(false)

                }

            })

        }

        function asKeydown() {

            $that.bind(‘focus‘, function() {

                var elem = $that[0]

                var val  = $that.val()

                if (val == word) {

                    setTimeout(function() {

                        // 光标定位到首位

                        $that.setCursorPosition({index: 0})

                    }, 10)                 

                }

            })

        }

        if (evtType == ‘focus‘) {

            asFocus()

        } else if (evtType == ‘keydown‘) {

            asKeydown()

        }

        // keydown事件里处理placeholder

        $that.keydown(function() {

            var val = $that.val()

            if (val == word) {

                switchStatus(true)

            }

        }).keyup(function() {

            var val = $that.val()

            if (val == ‘‘) {

                switchStatus(false)

                $that.setCursorPosition({index: 0})

            }

        })

    }

    return this.each(function() {

        var $elem = $(this)

        bootstrap($elem)

        if ($.isFunction(callback)) callback($elem)

    })

}

线上示例:http://snandy.github.io/lib/ui/demo/placeholder/b.html

会用到 setCursorPosition

方式二


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

$.fn.placeholder = function(option, callback) {

    var settings = $.extend({

        word: ‘‘,

        color: ‘#999‘,

        evtType: ‘focus‘,

        zIndex: 20,

        diffPaddingLeft: 3

    }, option)

    function bootstrap($that) {

        // some alias

        var word    = settings.word

        var color   = settings.color

        var evtType = settings.evtType

        var zIndex  = settings.zIndex

        var diffPaddingLeft = settings.diffPaddingLeft

        // default css

        var width       = $that.outerWidth()

        var height      = $that.outerHeight()

        var fontSize    = $that.css(‘font-size‘)

        var fontFamily  = $that.css(‘font-family‘)

        var paddingLeft = $that.css(‘padding-left‘)

        // process

        paddingLeft = parseInt(paddingLeft, 10) + diffPaddingLeft

        // redner

        var $placeholder = $(‘<span class="placeholder">‘)

        $placeholder.css({

            position: ‘absolute‘,

            zIndex: ‘20‘,

            color: color,

            width: (width - paddingLeft) + ‘px‘,

            height: height + ‘px‘,

            fontSize: fontSize,

            paddingLeft: paddingLeft + ‘px‘,

            fontFamily: fontFamily

        }).text(word).hide()

        // 位置调整

        move()

        // textarea 不加line-heihgt属性

        if ($that.is(‘input‘)) {

            $placeholder.css({

                lineHeight: height + ‘px‘

            })

        }

        $placeholder.appendTo(document.body)

        // 内容为空时才显示,比如刷新页面输入域已经填入了内容时

        var val = $that.val()

        if ( val == ‘‘ && $that.is(‘:visible‘) ) {

            $placeholder.show()

        }

        function hideAndFocus() {

            $placeholder.hide()

            $that[0].focus()

        }

        function move() {

            var offset = $that.offset()

            var top    = offset.top

            var left   = offset.left

            $placeholder.css({

                top: top,

                left: left

            })

        }

        function asFocus() {

            $placeholder.click(function() {

                hideAndFocus()

                // 盖住后无法触发input的click事件,需要模拟点击下

                setTimeout(function(){

                    $that.click()

                }, 100)

            })

            // IE有些bug,原本不用加此句

            $that.click(hideAndFocus)

            $that.blur(function() {

                var txt = $that.val()

                if (txt == ‘‘) {

                    $placeholder.show()

                }

            })

        }

        function asKeydown() {

            $placeholder.click(function() {

                $that[0].focus()

            })

        }

        if (evtType == ‘focus‘) {

            asFocus()

        } else if (evtType == ‘keydown‘) {

            asKeydown()

        }

        $that.keyup(function() {

            var txt = $that.val()

            if (txt == ‘‘) {

                $placeholder.show()

            } else {

                $placeholder.hide()

            }

        })

        // 窗口缩放时处理

        $(window).resize(function() {

            move()

        })

        // cache

        $that.data(‘el‘, $placeholder)

        $that.data(‘move‘, move)

    }

    return this.each(function() {

        var $elem = $(this)

        bootstrap($elem)

        if ($.isFunction(callback)) callback($elem)

    })

}  

线上示例:http://snandy.github.io/lib/ui/demo/placeholder/a.html

方式2 对于以下场景不适合

1. input初始隐藏

  此时无法取到input的offset,继而无法定位span到input上面。

2. 包含input的页面dom结构发生变化

  比如页面里删除了一些元素或添加了一些元素,导致input向上或向下偏移,而此时span则没有偏移(span相对body定位)。这比较恶心,可以考虑把span作为input的兄弟元素,即相对内层div定位(而不是body)。但这样必须强制给外层div添加position:relative,添加后可能会对页面布局产生一定影响。

相关:

http://www.w3.org/TR/2009/WD-html5-20090825/forms.html#the-placeholder-attribute

https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-placeholder

时间: 2024-10-26 22:01:17

PlaceHolder的两种实现方式的相关文章

简易版聊天系统实现 Socket VS NIO两种实现方式

说是简单聊天系统,压根不能算是一个系统,顶多算个雏形.本文重点不在聊天系统设计和实现上,而是通过实现类似效果,展示下NIO 和Socket两种编程方式的差异性.说是Socket与NIO的编程方式,不太严谨,因为NIO的底层也是通过Socket实现的,但又想不出非常好的题目,就这样吧. 主要内容 Socket方式实现简易聊天效果 NIO方式实现简易聊天效果 两种方式的性能对比 前言 预期效果,是客户端之间进行"广播"式聊天,类似于QQ群聊天.希望以后有机会,以此简易版为基础,不断演进,演

Redis两种持久化方式(RDB&amp;AOF)

爬虫和转载请注明原文地址;博客园蜗牛:http://www.cnblogs.com/tdws/p/5754706.html Redis所需内存 超过可用内存怎么办 Redis修改数据多线程并发—Redis并发锁 windows下redis基础操作与主从复制 从而 数据备份和读写分离 Redis两种持久化方式(RDB&AOF) Redis的持久化过程中并不需要我们开发人员过多的参与,我们要做的是什么呢?除了深入了解RDB和AOF的作用原理,剩下的就是根据实际情况来制定合适的策略了,再复杂一点,也就

冒泡排序及两种优化方式

冒泡排序是最常用的小型数据排序方式,下面是用C语言实现的,及其两种优化方式. 第一种优化方式是设置一个标记位来标记是否发生了交换,如果没有发生交换就提前结束: 第二种优化方式是记录最后放生交换的位置,作为下一趟比较结束的位置. #include <stdio.h> /* * 打印数组 * */ void printArray(int arr[], int n) { int i = 0; for (i = 0; i < n; ++i) { printf("%d ", a

struts2+spring的两种整合方式

借助于Spring插件(Struts2-spring-plugin-XXX.jar),我们可以非常简单地完成Spring和Struts2的整合,这种整合包括让Action自动装配Spring容器中的Bean,以及让Spring管理应用中的Action两种方式,不管采用哪种方式,完成Struts2和Spring的整合都是非常简单的,而且差别不大.一旦在Web应用中安装了Spring插件,即可充分利用该插件提供的功能: 1,可以通过Spring来创建所有的Action,Interceptor和Res

两种出错处理方式

两种出错处理方式:一种是对出错函数进行重定义,一种是对错误进行捕捉处理. ;;=================================================================================================== ;;=================================================================================================== ;;=======

JAVABEAN是什么和总结JAVABEAN的两种使用方式

看完这个后再也不纠结javabean是什么东西了,感谢博主,由于是Javablog不能收藏故在此转发. 以下内容转自:http://www.blogjava.net/flysky19/articles/88180.html 一. javabean 是什么? Javabean 就是一个类,这个类就定义一系列 get<Name> 和 set<Name> 方法. So simple ! Javabean 就是为了和 jsp 页面传数据化简交互过程而产生的. 自己的理解: 使用 javab

细说java中Map的两种迭代方式

以前对java中迭代方式总是迷迷糊糊的,今天总算弄懂了,特意的总结了一下,基本是算是理解透彻了. 1.再说Map之前先说下Iterator: Iterator主要用于遍历(即迭代访问)Collection集合中的元素,Iterator也称为迭代器.它仅仅只有三个方法:hasNext(),next()和remove() hasNext():如果仍有元素可以迭代,则返回 true.(换句话说,如果 next 返回了元素而不是 抛出异常,则返回 true). next():返回迭代的下一个元素. re

java 程序执行输出有两种简单方式

java 程序执行输出有两种简单方式: 1. System.out.println("需要输出的内容"): 该方法可参看运行一个简单的Java程序 结果图: 2. System.out.print("需要输出的内容"): 1 public class HelloWorld 2 { 3 //Java程序的入口方法,程序将从这里开始运行 4 public static void main(String[] args) 5 { 6 //向控制台打印一条语句 7 Syste

Android中两种序列化方式的比较Serializable和Parcelable

Serializable和Parcelable接口可以完成对象的序列化过程,当我们需要通过Intent和Binder传输数据时就需要使用者两种序列化方式.还有,我们需要对象持久化到存储设备或者通过网络传输给其他客户端,这个使用也需要使用Serializale来完成对象的序列化.在Android应用开发中,这两种方式都很常见,但两者方式并不相同. 1.Serializable接口 Serializable接口是Java提供的一个序列化接口,它是一个空接口,为对象提供标准的序列化和反序列化操作.使用