UNPKG

8.72 kBJavaScriptView Raw
1/**
2* HTML5 placeholder polyfill
3* @requires jQuery - tested with 1.6.2 but might as well work with older versions
4*
5* code: https://github.com/ginader/HTML5-placeholder-polyfill
6* please report issues at: https://github.com/ginader/HTML5-placeholder-polyfill/issues
7*
8* Copyright (c) 2012 Dirk Ginader (ginader.de)
9* Dual licensed under the MIT and GPL licenses:
10* http://www.opensource.org/licenses/mit-license.php
11* http://www.gnu.org/licenses/gpl.html
12*
13*/
14
15(function($) {
16 var debug = false,
17 animId;
18 function showPlaceholderIfEmpty(input,options) {
19 if( input.val() === '' ){
20 input.data('placeholder').removeClass(options.hideClass);
21 }else{
22 input.data('placeholder').addClass(options.hideClass);
23 }
24 }
25 function hidePlaceholder(input,options){
26 input.data('placeholder').addClass(options.hideClass);
27 }
28 function positionPlaceholder(placeholder,input){
29 var ta = input.is('textarea');
30 var pt = parseFloat(input.css('padding-top'));
31 var pl = parseFloat(input.css('padding-left'));
32
33 // Determine if we need to shift the header down more.
34 var offset = input.offset();
35 if (pt) {
36 offset.top += pt;
37 }
38 if (pl) {
39 offset.left += pl;
40 }
41
42 placeholder.css({
43 width : input.innerWidth()-(ta ? 20 : 4),
44 height : input.innerHeight()-6,
45 lineHeight : input.css('line-height'),
46 whiteSpace : ta ? 'normal' : 'nowrap',
47 overflow : 'hidden'
48 }).offset(offset);
49 }
50 function startFilledCheckChange(input,options){
51 var val = input.val();
52 (function checkloop(){
53 animId = requestAnimationFrame(checkloop);
54 if(input.val() !== val){
55 hidePlaceholder(input,options);
56 stopCheckChange();
57 startEmptiedCheckChange(input,options);
58 }
59 }());
60 }
61 function startEmptiedCheckChange(input,options){
62 (function checkloop(){
63 animId = requestAnimationFrame(checkloop);
64 showPlaceholderIfEmpty(input,options);
65 }());
66 }
67 function stopCheckChange(){
68 if (window.cancelAnimationFrame) {
69 cancelAnimationFrame(animId);
70 }
71 }
72 function log(msg){
73 if(debug && window.console && window.console.log){
74 window.console.log(msg);
75 }
76 }
77
78 $.fn.placeHolder = function(config) {
79 log('init placeHolder');
80 var o = this;
81 var l = $(this).length;
82 this.options = $.extend({
83 className: 'placeholder', // css class that is used to style the placeholder
84 visibleToScreenreaders : true, // expose the placeholder text to screenreaders or not
85 visibleToScreenreadersHideClass : 'placeholder-hide-except-screenreader', // css class is used to visually hide the placeholder
86 visibleToNoneHideClass : 'placeholder-hide', // css class used to hide the placeholder for all
87 hideOnFocus : false, // either hide the placeholder on focus or on type
88 removeLabelClass : 'visuallyhidden', // remove this class from a label (to fix hidden labels)
89 hiddenOverrideClass : 'visuallyhidden-with-placeholder', // replace the label above with this class
90 forceHiddenOverride : true, // allow the replace of the removeLabelClass with hiddenOverrideClass or not
91 forceApply : false, // apply the polyfill even for browser with native support
92 autoInit : true // init automatically or not
93 }, config);
94 this.options.hideClass = this.options.visibleToScreenreaders ? this.options.visibleToScreenreadersHideClass : this.options.visibleToNoneHideClass;
95 return $(this).each(function(index) {
96 var input = $(this),
97 text = input.attr('placeholder'),
98 id = input.attr('id'),
99 label,placeholder,titleNeeded,polyfilled;
100
101 function onFocusIn() {
102 if(!o.options.hideOnFocus && window.requestAnimationFrame){
103 startFilledCheckChange(input,o.options);
104 }else{
105 hidePlaceholder(input,o.options);
106 }
107 }
108
109 if(text === "" || text === undefined) {
110 text = input[0].attributes["placeholder"].value;
111 }
112 label = input.closest('label');
113 input.removeAttr('placeholder');
114 if(!label.length && !id){
115 log('the input element with the placeholder needs an id!');
116 return;
117 }
118 label = label.length ? label : $('label[for="'+id+'"]').first();
119 if(!label.length){
120 log('the input element with the placeholder needs a label!');
121 return;
122 }
123 polyfilled = $(label).find('.placeholder');
124 if(polyfilled.length) {
125 //log('the input element already has a polyfilled placeholder!');
126 positionPlaceholder(polyfilled,input);
127 polyfilled.text(text);
128 return input;
129 }
130 if(label.hasClass(o.options.removeLabelClass)){
131 label.removeClass(o.options.removeLabelClass)
132 .addClass(o.options.hiddenOverrideClass);
133 }
134
135 placeholder = $('<span>').addClass(o.options.className).text(text).appendTo(label);
136
137 titleNeeded = (placeholder.width() > input.width());
138 if(titleNeeded){
139 placeholder.attr('title',text);
140 }
141 positionPlaceholder(placeholder,input);
142 input.data('placeholder',placeholder);
143 placeholder.data('input',input);
144 placeholder.click(function(){
145 $(this).data('input').focus();
146 });
147 input.focusin(onFocusIn);
148 input.focusout(function(){
149 showPlaceholderIfEmpty($(this),o.options);
150 if(!o.options.hideOnFocus){
151 stopCheckChange();
152 }
153 });
154 showPlaceholderIfEmpty(input,o.options);
155
156 // reformat on window resize and optional reformat on font resize - requires: http://www.tomdeater.com/jquery/onfontresize/
157 $(document).bind("fontresize resize", function(){
158 positionPlaceholder(placeholder,input);
159 });
160
161 // optional reformat when a textarea is being resized - requires http://benalman.com/projects/jquery-resize-plugin/
162 if($.event.special.resize){
163 $("textarea").bind("resize", function(event){
164 if ($(this).is(":visible")) {
165 positionPlaceholder(placeholder,input);
166 }
167 event.stopPropagation();
168 event.preventDefault();
169 });
170 }else{
171 // we simply disable the resizeablilty of textareas when we can't react on them resizing
172 $("textarea").css('resize','none');
173 }
174
175 if(index >= l-1 && typeof $.attrHooks !== 'undefined'){
176 $.attrHooks.placeholder = {
177 get: function(elem) {
178 if (elem.nodeName.toLowerCase() === 'input' || elem.nodeName.toLowerCase() === 'textarea') {
179 if( $(elem).data('placeholder') ){
180 // has been polyfilled
181 return $( $(elem).data('placeholder') ).text();
182 }else{
183 // native / not yet polyfilled
184 return $(elem)[0].placeholder;
185 }
186 }else{
187 return undefined;
188 }
189 },
190 set: function(elem, value){
191 return $( $(elem).data('placeholder') ).text(value);
192 }
193 };
194 }
195
196 if (input.is(":focus")) {
197 onFocusIn();
198 }
199 });
200
201
202
203 };
204 $(function(){
205 var config = window.placeHolderConfig || {};
206 if(config.autoInit === false){
207 log('placeholder:abort because autoInit is off');
208 return;
209 }
210 if(('placeholder' in $('<input>')[0] || 'placeHolder' in $('<input>')[0]) && !config.forceApply){ // don't run the polyfill when the browser has native support
211 log('placeholder:abort because browser has native support');
212 return;
213 }
214 $('input[placeholder], textarea[placeholder]').placeHolder(config);
215 });
216}(jQuery));