jquery.placeholder.js 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. ;(function(factory) {
  2. if (typeof define === 'function' && define.amd) {
  3. // AMD
  4. define(['jquery'], factory);
  5. } else if (typeof module === 'object' && module.exports) {
  6. factory(require('jquery'));
  7. } else {
  8. // Browser globals
  9. factory(jQuery);
  10. }
  11. }(function($) {
  12. /****
  13. * Allows plugin behavior simulation in modern browsers for easier debugging.
  14. * When setting to true, use attribute "placeholder-x" rather than the usual "placeholder" in your inputs/textareas
  15. * i.e. <input type="text" placeholder-x="my placeholder text" />
  16. */
  17. var debugMode = false;
  18. // Opera Mini v7 doesn't support placeholder although its DOM seems to indicate so
  19. var isOperaMini = Object.prototype.toString.call(window.operamini) === '[object OperaMini]';
  20. var isInputSupported = 'placeholder' in document.createElement('input') && !isOperaMini && !debugMode;
  21. var isTextareaSupported = 'placeholder' in document.createElement('textarea') && !isOperaMini && !debugMode;
  22. var valHooks = $.valHooks;
  23. var propHooks = $.propHooks;
  24. var hooks;
  25. var placeholder;
  26. var settings = {};
  27. if (isInputSupported && isTextareaSupported) {
  28. placeholder = $.fn.placeholder = function() {
  29. return this;
  30. };
  31. placeholder.input = true;
  32. placeholder.textarea = true;
  33. } else {
  34. placeholder = $.fn.placeholder = function(options) {
  35. var defaults = {customClass: 'placeholder'};
  36. settings = $.extend({}, defaults, options);
  37. return this.filter((isInputSupported ? 'textarea' : ':input') + '[' + (debugMode ? 'placeholder-x' : 'placeholder') + ']')
  38. .not('.'+settings.customClass)
  39. .not(':radio, :checkbox, :hidden')
  40. .bind({
  41. 'focus.placeholder': clearPlaceholder,
  42. 'blur.placeholder': setPlaceholder
  43. })
  44. .data('placeholder-enabled', true)
  45. .trigger('blur.placeholder');
  46. };
  47. placeholder.input = isInputSupported;
  48. placeholder.textarea = isTextareaSupported;
  49. hooks = {
  50. 'get': function(element) {
  51. var $element = $(element);
  52. var $passwordInput = $element.data('placeholder-password');
  53. if ($passwordInput) {
  54. return $passwordInput[0].value;
  55. }
  56. return $element.data('placeholder-enabled') && $element.hasClass(settings.customClass) ? '' : element.value;
  57. },
  58. 'set': function(element, value) {
  59. var $element = $(element);
  60. var $replacement;
  61. var $passwordInput;
  62. if (value !== '') {
  63. $replacement = $element.data('placeholder-textinput');
  64. $passwordInput = $element.data('placeholder-password');
  65. if ($replacement) {
  66. clearPlaceholder.call($replacement[0], true, value) || (element.value = value);
  67. $replacement[0].value = value;
  68. } else if ($passwordInput) {
  69. clearPlaceholder.call(element, true, value) || ($passwordInput[0].value = value);
  70. element.value = value;
  71. }
  72. }
  73. if (!$element.data('placeholder-enabled')) {
  74. element.value = value;
  75. return $element;
  76. }
  77. if (value === '') {
  78. element.value = value;
  79. // Setting the placeholder causes problems if the element continues to have focus.
  80. if (element != safeActiveElement()) {
  81. // We can't use `triggerHandler` here because of dummy text/password inputs :(
  82. setPlaceholder.call(element);
  83. }
  84. } else {
  85. if ($element.hasClass(settings.customClass)) {
  86. clearPlaceholder.call(element);
  87. }
  88. element.value = value;
  89. }
  90. // `set` can not return `undefined`; see http://jsapi.info/jquery/1.7.1/val#L2363
  91. return $element;
  92. }
  93. };
  94. if (!isInputSupported) {
  95. valHooks.input = hooks;
  96. propHooks.value = hooks;
  97. }
  98. if (!isTextareaSupported) {
  99. valHooks.textarea = hooks;
  100. propHooks.value = hooks;
  101. }
  102. $(function() {
  103. // Look for forms
  104. $(document).delegate('form', 'submit.placeholder', function() {
  105. // Clear the placeholder values so they don't get submitted
  106. var $inputs = $('.'+settings.customClass, this).each(function() {
  107. clearPlaceholder.call(this, true, '');
  108. });
  109. setTimeout(function() {
  110. $inputs.each(setPlaceholder);
  111. }, 10);
  112. });
  113. });
  114. // Clear placeholder values upon page reload
  115. $(window).bind('beforeunload.placeholder', function() {
  116. var clearPlaceholders = true;
  117. try {
  118. // Prevent IE javascript:void(0) anchors from causing cleared values
  119. if (document.activeElement.toString() === 'javascript:void(0)') {
  120. clearPlaceholders = false;
  121. }
  122. } catch (exception) { }
  123. if (clearPlaceholders) {
  124. $('.'+settings.customClass).each(function() {
  125. //this.value = '';
  126. });
  127. }
  128. });
  129. }
  130. function args(elem) {
  131. // Return an object of element attributes
  132. var newAttrs = {};
  133. var rinlinejQuery = /^jQuery\d+$/;
  134. $.each(elem.attributes, function(i, attr) {
  135. if (attr.specified && !rinlinejQuery.test(attr.name)) {
  136. newAttrs[attr.name] = attr.value;
  137. }
  138. });
  139. return newAttrs;
  140. }
  141. function clearPlaceholder(event, value) {
  142. var input = this;
  143. var $input = $(this);
  144. if (input.value === $input.attr((debugMode ? 'placeholder-x' : 'placeholder')) && $input.hasClass(settings.customClass)) {
  145. input.value = '';
  146. $input.removeClass(settings.customClass);
  147. if ($input.data('placeholder-password')) {
  148. $input = $input.hide().nextAll('input[type="password"]:first').show().attr('id', $input.removeAttr('id').data('placeholder-id'));
  149. // If `clearPlaceholder` was called from `$.valHooks.input.set`
  150. if (event === true) {
  151. $input[0].value = value;
  152. return value;
  153. }
  154. $input.focus();
  155. } else {
  156. input == safeActiveElement() && input.select();
  157. }
  158. }
  159. }
  160. function setPlaceholder(event) {
  161. var $replacement;
  162. var input = this;
  163. var $input = $(this);
  164. var id = input.id;
  165. // If the placeholder is activated, triggering blur event (`$input.trigger('blur')`) should do nothing.
  166. if (event && event.type === 'blur' && $input.hasClass(settings.customClass)) {
  167. return;
  168. }
  169. if (input.value === '') {
  170. if (input.type === 'password') {
  171. if (!$input.data('placeholder-textinput')) {
  172. try {
  173. $replacement = $input.clone().prop({ 'type': 'text' });
  174. } catch(e) {
  175. $replacement = $('<input>').attr($.extend(args(this), { 'type': 'text' }));
  176. }
  177. $replacement
  178. .removeAttr('name')
  179. .data({
  180. 'placeholder-enabled': true,
  181. 'placeholder-password': $input,
  182. 'placeholder-id': id
  183. })
  184. .bind('focus.placeholder', clearPlaceholder);
  185. $input
  186. .data({
  187. 'placeholder-textinput': $replacement,
  188. 'placeholder-id': id
  189. })
  190. .before($replacement);
  191. }
  192. input.value = '';
  193. $input = $input.removeAttr('id').hide().prevAll('input[type="text"]:first').attr('id', $input.data('placeholder-id')).show();
  194. } else {
  195. var $passwordInput = $input.data('placeholder-password');
  196. if ($passwordInput) {
  197. $passwordInput[0].value = '';
  198. $input.attr('id', $input.data('placeholder-id')).show().nextAll('input[type="password"]:last').hide().removeAttr('id');
  199. }
  200. }
  201. $input.addClass(settings.customClass);
  202. $input[0].value = $input.attr((debugMode ? 'placeholder-x' : 'placeholder'));
  203. } else {
  204. $input.removeClass(settings.customClass);
  205. }
  206. }
  207. function safeActiveElement() {
  208. // Avoid IE9 `document.activeElement` of death
  209. try {
  210. return document.activeElement;
  211. } catch (exception) {}
  212. }
  213. }));
  214. $(function(){
  215. $('input[placeholder], textarea[placeholder]').placeholder();
  216. });