forked from cvn/angular-shims-placeholder
-
Notifications
You must be signed in to change notification settings - Fork 1
/
angular-placeholder.js
178 lines (155 loc) · 4.79 KB
/
angular-placeholder.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
/*
* angular-shims-placeholder
* https://github.com/jrief/angular-shims-placeholder
*
* Add Angular directives which emulates attribute ´placeholder´ in input fields
* of type text for browsers not supporting this, ie. IE9 and below.
*
* Copyright (c) 2013 Jacob Rief
* Licensed under the MIT license.
*/
(function(angular, document, undefined) {
'use strict';
angular.module('ng.shims.placeholder', [])
.service('placeholderSniffer', function($document){
this.hasPlaceholder = function() {
// test for native placeholder support
var test = $document[0].createElement("input");
return (test.placeholder !== void 0);
};
})
.directive('placeholder', function($timeout, placeholderSniffer) {
if (placeholderSniffer.hasPlaceholder()) return {};
// No native support for attribute placeholder
return {
restrict: 'A',
require: '?ngModel',
priority: 1,
link: function(scope, elem, attrs, ngModel) {
var orig_val = getValue(),
domElem = elem[0],
elemType = domElem.nodeName.toLowerCase(),
isInput = elemType === 'input' || elemType === 'textarea',
is_pwd = attrs.type === 'password',
text = attrs.placeholder,
emptyClassName = 'empty',
clone;
if (!text || !isInput) { return; }
if (is_pwd) { setupPasswordPlaceholder(); }
// init
setValue(orig_val);
// on focus, replace auto-label with empty field
elem.bind('focus', function() {
if (elem.hasClass(emptyClassName)) {
elem.val('');
elem.removeClass(emptyClassName);
domElem.select(); // IE8/9 show text cursor after tabbing in
}
});
// on blur, show placeholder if necessary
elem.bind('blur', updateValue);
// handler for model-less inputs to interact with non-angular code
if (!ngModel) {
elem.bind('change', updateValue);
}
// model -> view
if (ngModel) {
ngModel.$render = function() {
setValue(ngModel.$viewValue);
// IE8/9: show text cursor after updating value while
// focused, this happens when tabbing into a field, and the
// deferred keydown handler from the previous field fires
//
// TODO: remove when tab key behavior is fixed in
// angular core
if (domElem === document.activeElement && !elem.val()) {
domElem.select();
}
};
}
function updateValue(e) {
var val = elem.val();
// don't update from placeholder, helps debounce
if (elem.hasClass(emptyClassName) && val === text) { return; }
// IE8/9: ngModel uses a keydown handler with deferrered
// execution to check for changes to the input. this $timeout
// prevents updateValue from firing before the keydown handler,
// which is an issue when tabbing out of an input.
// the conditional tests IE version, matches $sniffer.
//
// TODO: remove timeout path when tab key behavior is fixed in
// angular core
if (document.documentMode <= 11) {
$timeout(function(){
setValue(val);
},0);
} else {
setValue(val);
}
}
function setValue(val) {
if (!val && domElem !== document.activeElement) {
// show placeholder when necessary
elem.addClass(emptyClassName);
if (is_pwd) {
showPasswordPlaceholder();
} else {
elem.val(text);
}
} else {
// otherwise set input to actual value
elem.removeClass(emptyClassName);
if (is_pwd) {
hidePasswordPlaceholder();
}
elem.val(val);
}
}
function getValue() {
if (ngModel) {
return scope.$eval(attrs.ngModel) || '';
}
return getDomValue() || '';
}
// IE8/9: elem.val() on an empty field sometimes returns the
// placeholder value, so return an empty string instead
// http://stackoverflow.com/q/11208417/490592
function getDomValue() {
var val = elem.val();
if (val === attrs.placeholder) {
val = '';
}
return val;
}
// IE8: password inputs cannot display text, and inputs cannot
// change type, so create a new element to display placeholder
function setupPasswordPlaceholder() {
clone = angular.element('<input type="text" value="'+text+'"/>');
stylePasswordPlaceholder();
clone.addClass(emptyClassName)
.addClass('ng-hide')
.bind('focus', hidePasswordPlaceholderAndFocus);
domElem.parentNode.insertBefore(clone[0], domElem);
}
function stylePasswordPlaceholder() {
clone.val(text)
.attr('class', elem.attr('class') || '')
.attr('style', elem.attr('style') || '');
}
function showPasswordPlaceholder() {
stylePasswordPlaceholder();
elem.addClass('ng-hide');
clone.removeClass('ng-hide');
}
function hidePasswordPlaceholder() {
clone.addClass('ng-hide');
elem.removeClass('ng-hide');
}
function hidePasswordPlaceholderAndFocus() {
hidePasswordPlaceholder();
domElem.focus();
}
}
};
});
})(window.angular, window.document);