forked from ginader/HTML5-placeholder-polyfill
-
Notifications
You must be signed in to change notification settings - Fork 0
/
placeholder_polyfill.jquery.js
211 lines (199 loc) · 9.52 KB
/
placeholder_polyfill.jquery.js
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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
/**
* HTML5 placeholder polyfill
* @requires jQuery - tested with 1.6.2 but might as well work with older versions
*
* code: https://github.com/ginader/HTML5-placeholder-polyfill
* please report issues at: https://github.com/ginader/HTML5-placeholder-polyfill/issues
*
* Copyright (c) 2012 Dirk Ginader (ginader.de)
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
* Version: 2.0.2
*
* History:
* * 1.0 initial release
* * 1.1 added support for multiline placeholders in textareas
* * 1.2 Allow label to wrap the input element by noah https://github.com/ginader/HTML5-placeholder-polyfill/pull/1
* * 1.3 New option to read placeholder to Screenreaders. Turned on by default
* * 1.4 made placeholder more rubust to allow labels being offscreen + added minified version of the 3rd party libs
* * 1.5 emptying the native placeholder to prevent double rendering in Browsers with partial support
* * 1.6 optional reformat when a textarea is being resized - requires http://benalman.com/projects/jquery-resize-plugin/
* * 1.7 feature detection is now included in the polyfill so you can simply include it without the need for Modernizr
* * 1.8 replacing the HTML5 Boilerplate .visuallyhidden technique with one that still allows the placeholder to be rendered
* * 1.8.1 bugfix for implicit labels
* * 1.9 New option "hideOnFocus" which, if set to false will mimic the behavior of mobile safari and chrome (remove label when typed instead of onfocus)
* * 1.9.1 added reformat event on window resize
* * 1.9.2 more flexible way to "fix" labels that are hidden using clip() thanks to grahambates: https://github.com/ginader/HTML5-placeholder-polyfill/issues/12
* * 2.0 new easier configuration technique and new options forceApply and AutoInit and support for setters and getters
* * 2.0.1 changed check for empty field so a space character is no longer ignored
* * 2.0.2 allow rerun of the placeholder() to cover generated elements - existing polyfilled placeholder will be repositioned. Fixing: https://github.com/ginader/HTML5-placeholder-polyfill/issues/15
*/
(function($) {
var debug = true,
animId;
function showPlaceholderIfEmpty(input,options) {
if( input.val() === '' ){
input.data('placeholder').removeClass(options.hideClass);
}else{
input.data('placeholder').addClass(options.hideClass);
}
}
function hidePlaceholder(input,options){
input.data('placeholder').addClass(options.hideClass);
}
function positionPlaceholder(placeholder,input){
var ta = input.is('textarea');
placeholder.css({
width : input.innerWidth()-(ta ? 20 : 4),
height : input.innerHeight()-6,
lineHeight : input.css('line-height'),
whiteSpace : ta ? 'normal' : 'nowrap',
overflow : 'hidden'
}).offset(input.offset());
}
function startFilledCheckChange(input,options){
var input = input,
val = input.val();
(function checkloop(){
animId = requestAnimationFrame(checkloop);
if(input.val() != val){
hidePlaceholder(input,options);
stopCheckChange();
startEmptiedCheckChange(input,options);
}
})();
}
function startEmptiedCheckChange(input,options){
var input = input,
val = input.val();
(function checkloop(){
animId = requestAnimationFrame(checkloop);
showPlaceholderIfEmpty(input,options);
})();
}
function stopCheckChange(){
cancelAnimationFrame(animId);
}
function log(msg){
if(debug && window.console && window.console.log){
window.console.log(msg);
}
}
$.fn.placeHolder = function(config) {
log('init placeHolder');
var o = this;
var l = $(this).length;
this.options = $.extend({
className: 'placeholder', // css class that is used to style the placeholder
visibleToScreenreaders : true, // expose the placeholder text to screenreaders or not
visibleToScreenreadersHideClass : 'placeholder-hide-except-screenreader', // css class is used to visually hide the placeholder
visibleToNoneHideClass : 'placeholder-hide', // css class used to hide the placeholder for all
hideOnFocus : false, // either hide the placeholder on focus or on type
removeLabelClass : 'visuallyhidden', // remove this class from a label (to fix hidden labels)
hiddenOverrideClass : 'visuallyhidden-with-placeholder', // replace the label above with this class
forceHiddenOverride : true, // allow the replace of the removeLabelClass with hiddenOverrideClass or not
forceApply : false, // apply the polyfill even for browser with native support
autoInit : true // init automatically or not
}, config);
this.options.hideClass = this.options.visibleToScreenreaders ? this.options.visibleToScreenreadersHideClass : this.options.visibleToNoneHideClass;
return $(this).each(function(index) {
var input = $(this),
text = input.attr('placeholder'),
id = input.attr('id'),
label,placeholder,titleNeeded,polyfilled;
label = input.closest('label');
input.removeAttr('placeholder');
if(!label.length && !id){
log('the input element with the placeholder needs an id!');
return;
}
label = label.length ? label : $('label[for="'+id+'"]').first();
if(!label.length){
log('the input element with the placeholder needs a label!');
return;
}
polyfilled = $(label).find('.placeholder');
if(polyfilled.length) {
//log('the input element already has a polyfilled placeholder!');
positionPlaceholder(polyfilled,input);
return input;
}
if(label.hasClass(o.options.removeLabelClass)){
label.removeClass(o.options.removeLabelClass)
.addClass(o.options.hiddenOverrideClass);
}
placeholder = $('<span class="'+o.options.className+'">'+text+'</span>').appendTo(label);
titleNeeded = (placeholder.width() > input.width());
if(titleNeeded){
placeholder.attr('title',text);
}
positionPlaceholder(placeholder,input);
input.data('placeholder',placeholder);
placeholder.data('input',placeholder);
placeholder.click(function(){
$(this).data('input').focus();
});
input.focusin(function() {
if(!o.options.hideOnFocus && window.requestAnimationFrame){
startFilledCheckChange(input,o.options);
}else{
hidePlaceholder(input,o.options);
}
});
input.focusout(function(){
showPlaceholderIfEmpty($(this),o.options);
if(!o.options.hideOnFocus && window.cancelAnimationFrame){
stopCheckChange();
}
});
showPlaceholderIfEmpty(input,o.options);
// reformat on window resize and optional reformat on font resize - requires: http://www.tomdeater.com/jquery/onfontresize/
$(document).bind("fontresize resize", function(){
positionPlaceholder(placeholder,input);
});
// optional reformat when a textarea is being resized - requires http://benalman.com/projects/jquery-resize-plugin/
if($.event.special.resize){
$("textarea").bind("resize", function(e){
positionPlaceholder(placeholder,input);
});
}else{
// we simply disable the resizeablilty of textareas when we can't react on them resizing
$("textarea").css('resize','none');
}
if(index >= l-1){
$.attrHooks.placeholder = {
get: function(elem) {
if (elem.nodeName.toLowerCase() == 'input' || elem.nodeName.toLowerCase() == 'textarea') {
if( $(elem).data('placeholder') ){
// has been polyfilled
return $( $(elem).data('placeholder') ).text();
}else{
// native / not yet polyfilled
return $(elem)[0].placeholder;
}
}else{
return undefined;
}
},
set: function(elem, value){
return $( $(elem).data('placeholder') ).text(value);
}
};
}
});
};
$(function(){
var config = window.placeHolderConfig || {};
if(config.autoInit === false){
log('placeholder:abort because autoInit is off');
return
}
if('placeholder' in $('<input>')[0] && !config.forceApply){ // don't run the polyfill when the browser has native support
log('placeholder:abort because browser has native support');
return;
}
$('input[placeholder], textarea[placeholder]').placeHolder(config);
});
})(jQuery);