*
* This will automatically use the HTML5 placeholder attribute if supported, or emulate this behavior if not.
*
+ * This is a fork from Mathias Bynens' jquery.placeholder as of this commit
+ * https://github.com/mathiasbynens/jquery-placeholder/blob/47f05d400e2dd16b59d144141a2cf54a9a77c502/jquery.placeholder.js
+ *
+ * @author Mathias Bynens <http://mathiasbynens.be/>
* @author Trevor Parscal <tparscal@wikimedia.org>, 2012
* @author Krinkle <krinklemail@gmail.com>, 2012
- * @version 0.2.0
+ * @author Alex Ivanov <alexivanov97@gmail.com>, 2013
+ * @version 2.1.0
* @license MIT
*/
-( function ( $ ) {
+(function($) {
- $.fn.placeholder = function ( text ) {
- var hasArg = arguments.length;
+ var isInputSupported = 'placeholder' in document.createElement('input'),
+ isTextareaSupported = 'placeholder' in document.createElement('textarea'),
+ prototype = $.fn,
+ valHooks = $.valHooks,
+ propHooks = $.propHooks,
+ hooks,
+ placeholder;
- return this.each( function () {
- var placeholder, $input;
+ if (isInputSupported && isTextareaSupported) {
- if ( hasArg ) {
- this.setAttribute( 'placeholder', text );
- }
+ placeholder = prototype.placeholder = function(text) {
+ var hasArgs = arguments.length;
- // If the HTML5 placeholder attribute is supported, use it
- if ( this.placeholder && 'placeholder' in document.createElement( this.tagName ) ) {
- return;
+ if( hasArgs ) {
+ changePlaceholder.call(this, text);
}
- placeholder = hasArg ? text : this.getAttribute( 'placeholder' );
- $input = $(this);
+ return this;
+ };
+
+ placeholder.input = placeholder.textarea = true;
+
+ } else {
- // Show initially, if empty
- if ( this.value === '' || this.value === placeholder ) {
- $input.addClass( 'placeholder' ).val( placeholder );
+ placeholder = prototype.placeholder = function(text) {
+ var $this = this,
+ hasArgs = arguments.length;
+
+ if(hasArgs) {
+ changePlaceholder.call(this, text);
}
- $input
- // Show on blur if empty
- .blur( function () {
- if ( this.value === '' ) {
- this.value = placeholder;
- $input.addClass( 'placeholder' );
- }
- } )
-
- // Hide on focus
- // Also listen for other events in case $input was
- // already focused when the events were bound
- .on( 'focus drop keydown paste', function ( e ) {
- if ( $input.hasClass( 'placeholder' ) ) {
- if ( e.type === 'drop' && e.originalEvent.dataTransfer ) {
- // Support for drag&drop. Instead of inserting the dropped
- // text somewhere in the middle of the placeholder string,
- // we want to set the contents of the search box to the
- // dropped text.
-
- // IE wants getData( 'text' ) but Firefox wants getData( 'text/plain' )
- // Firefox fails gracefully with an empty string, IE barfs with an error
- try {
- // Try the Firefox way
- this.value = e.originalEvent.dataTransfer.getData( 'text/plain' );
- } catch ( exception ) {
- // Got an exception, so use the IE way
- this.value = e.originalEvent.dataTransfer.getData( 'text' );
- }
-
- // On Firefox, drop fires after the dropped text has been inserted,
- // but on IE it fires before. If we don't prevent the default action,
- // IE will insert the dropped text twice.
- e.preventDefault();
- } else {
- this.value = '';
- }
- $input.removeClass( 'placeholder' );
+
+ $this
+ .filter((isInputSupported ? 'textarea' : ':input') + '[placeholder]')
+ .filter(function() {
+ return !$(this).data('placeholder-enabled');
+ })
+ .bind({
+ 'focus.placeholder drop.placeholder': clearPlaceholder,
+ 'blur.placeholder': setPlaceholder
+ })
+ .data('placeholder-enabled', true)
+ .trigger('blur.placeholder');
+ return $this;
+ };
+
+ placeholder.input = isInputSupported;
+ placeholder.textarea = isTextareaSupported;
+
+ hooks = {
+ 'get': function(element) {
+ var $element = $(element),
+ $passwordInput = $element.data('placeholder-password');
+ if ($passwordInput) {
+ return $passwordInput[0].value;
+ }
+
+ return $element.data('placeholder-enabled') && $element.hasClass('placeholder') ? '' : element.value;
+ },
+ 'set': function(element, value) {
+ var $element = $(element),
+ $passwordInput = $element.data('placeholder-password');
+ if ($passwordInput) {
+ $passwordInput[0].value = value;
+ return value;
+ }
+
+ if (!$element.data('placeholder-enabled')) {
+ element.value = value;
+ return value;
+ }
+ if (!value) {
+ element.value = value;
+ // Issue #56: Setting the placeholder causes problems if the element continues to have focus.
+ if (element !== safeActiveElement()) {
+ // We can't use `triggerHandler` here because of dummy text/password inputs :(
+ setPlaceholder.call(element);
}
- } );
-
- // Blank on submit -- prevents submitting with unintended value
- if ( this.form ) {
- $( this.form ).submit( function () {
- // $input.trigger( 'focus' ); would be problematic
- // because it actually focuses $input, leading
- // to nasty behavior in mobile browsers
- if ( $input.hasClass( 'placeholder' ) ) {
- $input
- .val( '' )
- .removeClass( 'placeholder' );
+ } else if ($element.hasClass('placeholder')) {
+ if(!clearPlaceholder.call(element, true, value)) {
+ element.value = value;
}
- });
+ } else {
+ element.value = value;
+ }
+ // `set` can not return `undefined`; see http://jsapi.info/jquery/1.7.1/val#L2363
+ return $element;
}
+ };
+
+ if (!isInputSupported) {
+ valHooks.input = hooks;
+ propHooks.value = hooks;
+ }
+ if (!isTextareaSupported) {
+ valHooks.textarea = hooks;
+ propHooks.value = hooks;
+ }
+
+ $(function() {
+ // Look for forms
+ $(document).delegate('form', 'submit.placeholder', function() {
+ // Clear the placeholder values so they don't get submitted
+ var $inputs = $('.placeholder', this).each(clearPlaceholder);
+ setTimeout(function() {
+ $inputs.each(setPlaceholder);
+ }, 10);
+ });
+ });
+ // Clear placeholder values upon page reload
+ $(window).bind('beforeunload.placeholder', function() {
+ $('.placeholder').each(function() {
+ this.value = '';
+ });
});
- };
-}( jQuery ) );
+ }
+
+ function args(elem) {
+ // Return an object of element attributes
+ var newAttrs = {},
+ rinlinejQuery = /^jQuery\d+$/;
+ $.each(elem.attributes, function(i, attr) {
+ if (attr.specified && !rinlinejQuery.test(attr.name)) {
+ newAttrs[attr.name] = attr.value;
+ }
+ });
+ return newAttrs;
+ }
+
+ function clearPlaceholder(event, value) {
+ var input = this,
+ $input = $(input);
+ if (input.value === $input.attr('placeholder') && $input.hasClass('placeholder')) {
+ if ($input.data('placeholder-password')) {
+ $input = $input.hide().next().show().attr('id', $input.removeAttr('id').data('placeholder-id'));
+ // If `clearPlaceholder` was called from `$.valHooks.input.set`
+ if (event === true) {
+ $input[0].value = value;
+ return value;
+ }
+ $input.focus();
+ } else {
+ input.value = '';
+ $input.removeClass('placeholder');
+ if(input === safeActiveElement()) {
+ input.select();
+ }
+ }
+ }
+ }
+
+ function setPlaceholder() {
+ var $replacement,
+ input = this,
+ $input = $(input),
+ id = this.id;
+ if (!input.value) {
+ if (input.type === 'password') {
+ if (!$input.data('placeholder-textinput')) {
+ try {
+ $replacement = $input.clone().attr({ 'type': 'text' });
+ } catch(e) {
+ $replacement = $('<input>').attr($.extend(args(this), { 'type': 'text' }));
+ }
+ $replacement
+ .removeAttr('name')
+ .data({
+ 'placeholder-password': $input,
+ 'placeholder-id': id
+ })
+ .bind('focus.placeholder drop.placeholder', clearPlaceholder);
+ $input
+ .data({
+ 'placeholder-textinput': $replacement,
+ 'placeholder-id': id
+ })
+ .before($replacement);
+ }
+ $input = $input.removeAttr('id').hide().prev().attr('id', id).show();
+ // Note: `$input[0] != input` now!
+ }
+ $input.addClass('placeholder');
+ $input[0].value = $input.attr('placeholder');
+ } else {
+ $input.removeClass('placeholder');
+ }
+ }
+
+ function safeActiveElement() {
+ // Avoid IE9 `document.activeElement` of death
+ // https://github.com/mathiasbynens/jquery-placeholder/pull/99
+ try {
+ return document.activeElement;
+ } catch (err) {}
+ }
+
+ function changePlaceholder(text) {
+ var hasArgs = arguments.length,
+ $input = this;
+ if(hasArgs) {
+ if($input.attr('placeholder') !== text) {
+ $input.prop('placeholder', text);
+ if($input.hasClass('placeholder')) {
+ $input[0].value = text;
+ }
+ }
+ }
+ }
+
+}(jQuery));
--- /dev/null
+(function($) {
+
+ QUnit.module('jquery.placeholder', QUnit.newMwEnvironment());
+
+ QUnit.test('caches results of feature tests', 2, function(assert) {
+ assert.strictEqual(typeof $.fn.placeholder.input, 'boolean', '$.fn.placeholder.input');
+ assert.strictEqual(typeof $.fn.placeholder.textarea, 'boolean', '$.fn.placeholder.textarea');
+ });
+
+ if ($.fn.placeholder.input && $.fn.placeholder.textarea) {
+ return;
+ }
+
+ var html = '<form>' +
+ '<input id="input-type-search" type="search" placeholder="Search this site...">' +
+ '<input id="input-type-text" type="text" placeholder="e.g. John Doe">' +
+ '<input id="input-type-email" type="email" placeholder="e.g. address@example.ext">' +
+ '<input id="input-type-url" type="url" placeholder="e.g. http://mathiasbynens.be/">' +
+ '<input id="input-type-tel" type="tel" placeholder="e.g. +32 472 77 69 88">' +
+ '<input id="input-type-password" type="password" placeholder="e.g. hunter2">' +
+ '<textarea id="textarea" name="message" placeholder="Your message goes here"></textarea>' +
+ '</form>',
+ testElement = function($el, assert) {
+
+ var el = $el[0],
+ placeholder = el.getAttribute('placeholder');
+
+ assert.strictEqual($el.placeholder(), $el, 'should be chainable');
+
+ assert.strictEqual(el.value, placeholder, 'should set `placeholder` text as `value`');
+ assert.strictEqual($el.prop('value'), '', 'propHooks works properly');
+ assert.strictEqual($el.val(), '', 'valHooks works properly');
+ assert.ok($el.hasClass('placeholder'), 'should have `placeholder` class');
+
+ // test on focus
+ $el.focus();
+ assert.strictEqual(el.value, '', '`value` should be the empty string on focus');
+ assert.strictEqual($el.prop('value'), '', 'propHooks works properly');
+ assert.strictEqual($el.val(), '', 'valHooks works properly');
+ assert.ok(!$el.hasClass('placeholder'), 'should not have `placeholder` class on focus');
+
+ // and unfocus (blur) again
+ $el.blur();
+
+ assert.strictEqual(el.value, placeholder, 'should set `placeholder` text as `value`');
+ assert.strictEqual($el.prop('value'), '', 'propHooks works properly');
+ assert.strictEqual($el.val(), '', 'valHooks works properly');
+ assert.ok($el.hasClass('placeholder'), 'should have `placeholder` class');
+
+ // change the value
+ $el.val('lorem ipsum');
+ assert.strictEqual($el.prop('value'), 'lorem ipsum', '`$el.val(string)` should change the `value` property');
+ assert.strictEqual(el.value, 'lorem ipsum', '`$el.val(string)` should change the `value` attribute');
+ assert.ok(!$el.hasClass('placeholder'), '`$el.val(string)` should remove `placeholder` class');
+
+ // and clear it again
+ $el.val('');
+ assert.strictEqual($el.prop('value'), '', '`$el.val("")` should change the `value` property');
+ assert.strictEqual(el.value, placeholder, '`$el.val("")` should change the `value` attribute');
+ assert.ok($el.hasClass('placeholder'), '`$el.val("")` should re-enable `placeholder` class');
+
+ // make sure the placeholder property works as expected.
+ assert.strictEqual($el.prop('placeholder'), placeholder, '$el.prop(`placeholder`) should return the placeholder value');
+ $el.placeholder('new placeholder');
+ assert.strictEqual(el.getAttribute('placeholder'), 'new placeholder', '$el.placeholder(<string>) should set the placeholder value');
+ assert.strictEqual(el.value, 'new placeholder', '$el.placeholder(<string>) should update the displayed placeholder value');
+ $el.placeholder(placeholder);
+ };
+
+ QUnit.test('emulates placeholder for <input type=text>', 22, function(assert) {
+ $('<div>').html(html).appendTo($('#qunit-fixture'));
+ testElement($('#input-type-text'), assert);
+ });
+
+ QUnit.test('emulates placeholder for <input type=search>', 22, function(assert) {
+ $('<div>').html(html).appendTo($('#qunit-fixture'));
+ testElement($('#input-type-search'), assert);
+ });
+
+ QUnit.test('emulates placeholder for <input type=email>', 22, function(assert) {
+ $('<div>').html(html).appendTo($('#qunit-fixture'));
+ testElement($('#input-type-email'), assert);
+ });
+
+ QUnit.test('emulates placeholder for <input type=url>', 22, function(assert) {
+ $('<div>').html(html).appendTo($('#qunit-fixture'));
+ testElement($('#input-type-url'), assert);
+ });
+
+ QUnit.test('emulates placeholder for <input type=tel>', 22, function(assert) {
+ $('<div>').html(html).appendTo($('#qunit-fixture'));
+ testElement($('#input-type-tel'), assert);
+ });
+
+ QUnit.test('emulates placeholder for <input type=password>', 13, function(assert) {
+ $('<div>').html(html).appendTo($('#qunit-fixture'));
+
+ var selector = '#input-type-password',
+ $el = $(selector),
+ el = $el[0],
+ placeholder = el.getAttribute('placeholder');
+
+ assert.strictEqual($el.placeholder(), $el, 'should be chainable');
+
+ // Re-select the element, as it gets replaced by another one in some browsers
+ $el = $(selector);
+ el = $el[0];
+
+ assert.strictEqual(el.value, placeholder, 'should set `placeholder` text as `value`');
+ assert.strictEqual($el.prop('value'), '', 'propHooks works properly');
+ assert.strictEqual($el.val(), '', 'valHooks works properly');
+ assert.ok($el.hasClass('placeholder'), 'should have `placeholder` class');
+
+ // test on focus
+ $el.focus();
+
+ // Re-select the element, as it gets replaced by another one in some browsers
+ $el = $(selector);
+ el = $el[0];
+
+ assert.strictEqual(el.value, '', '`value` should be the empty string on focus');
+ assert.strictEqual($el.prop('value'), '', 'propHooks works properly');
+ assert.strictEqual($el.val(), '', 'valHooks works properly');
+ assert.ok(!$el.hasClass('placeholder'), 'should not have `placeholder` class on focus');
+
+ // and unfocus (blur) again
+ $el.blur();
+
+ // Re-select the element, as it gets replaced by another one in some browsers
+ $el = $(selector);
+ el = $el[0];
+
+ assert.strictEqual(el.value, placeholder, 'should set `placeholder` text as `value`');
+ assert.strictEqual($el.prop('value'), '', 'propHooks works properly');
+ assert.strictEqual($el.val(), '', 'valHooks works properly');
+ assert.ok($el.hasClass('placeholder'), 'should have `placeholder` class');
+
+ });
+
+ QUnit.test('emulates placeholder for <textarea></textarea>', 22, function(assert) {
+ $('<div>').html(html).appendTo($('#qunit-fixture'));
+ testElement($('#textarea'), assert);
+ });
+
+}(jQuery));