Skip to content

Commit

Permalink
Make .val() work as expected
Browse files Browse the repository at this point in the history
* Use `jQuery.valHooks` to make sure `.val()` and `.val(value)` work as expected. Avoid using `.val()` internally. This fixes #30.
* Tweak the syntax a bit here and there now that we’ve dropped support for pre-1.6 jQueries.
  • Loading branch information
mathiasbynens committed Feb 10, 2012
1 parent 06d8e67 commit b7a4199
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 27 deletions.
16 changes: 11 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

### HTML

``` html
```html
<input type="text" name="name" placeholder="e.g. John Doe">
<input type="email" name="email" placeholder="e.g. address@example.ext">
<input type="url" name="url" placeholder="e.g. http://mathiasbynens.be/">
Expand All @@ -20,28 +20,34 @@

### jQuery

``` js
Use the plugin as follows:

```js
$('input, textarea').placeholder();
```

You’ll still be able to use `jQuery#val()` to get and set the input values. If the element is currently showing a placeholder, `.val()` will return an empty string instead of the placeholder text, just like it does it browsers with a native `@placeholder` implementation. Calling `.val('')` to set an element’s value to the empty string will result in the placeholder text (re)appearing.

### CSS

The plugin automatically adds `class="placeholder"` to the elements who are currently showing their placeholder text. You can use this to style placeholder text differently:

``` css
```css
input, textarea { color: #000; }
.placeholder { color: #aaa; }
```

I’d suggest sticking to the `#aaa` color for placeholder text, as it’s the default in most browsers that support `@placeholder`. If you really want to, though, you can [style the placeholder text in some of the browsers that natively support it](http://stackoverflow.com/questions/2610497/change-an-inputs-html5-placeholder-color-with-css/2610741#2610741).

## Notes

* Requires jQuery 1.4.2+.
* Requires jQuery 1.6+. For an older version of this plugin that works under jQuery 1.4.2+, see [v1.8.7](https://github.com/mathiasbynens/jquery-placeholder/tree/1.8.7).
* Works in all A-grade browsers, including IE6.
* Automatically checks if the browser natively supports the HTML5 `placeholder` attribute for `input` and `textarea` elements. If this is the case, the plugin won’t do anything. If `@placeholder` is only supported for `input` elements, the plugin will leave those alone and apply to `textarea`s exclusively. (This is the case for Safari 4, Opera 11.00, and possibly other browsers.)
* Caches the results of its two feature tests in `jQuery.fn.placeholder.input` and `jQuery.fn.placeholder.textarea`. For example, if `@placeholder` is natively supported for `input` elements, `jQuery.fn.placeholder.input` will be `true`. After loading the plugin, you can re-use these properties in your own code.
* Makes sure it never causes duplicate IDs in your DOM, even in browsers that need an extra `input` element to fake `@placeholder` for password inputs. This means you can safely do stuff like:

``` html
```html
<label for="bar">Example label</label>
<input type="password" placeholder="foo" id="bar">
```
Expand Down
4 changes: 2 additions & 2 deletions demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ <h1>HTML5 Placeholder jQuery Plugin</h1>
<p><input type="submit" value="type=submit"></p>
</form>
<p><a href="http://mathiasbynens.be/" title="Mathias Bynens, front-end developer">Mathias</a></p>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.js"></script>
<script src="jquery.placeholder.js"></script>
<script>
// To test the @id toggling on password inputs in browsers that don’t support changing an input’s @type dynamically (e.g. Firefox 3.6 or IE), uncomment this:
// jQuery.fn.hide = function() { return this; }
// $.fn.hide = function() { return this; }
// Then uncomment the last rule in the <style> element (in the <head>).
$(function() {
// Invoke the plugin
Expand Down
79 changes: 61 additions & 18 deletions jquery.placeholder.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
/*! http://mths.be/placeholder v1.8.7 by @mathias */
/*! http://mths.be/placeholder v2.0.0 by @mathias */
;(function(window, document, $) {

var isInputSupported = 'placeholder' in document.createElement('input'),
isTextareaSupported = 'placeholder' in document.createElement('textarea'),
prototype = $.fn,
valHooks = $.valHooks,
hooks,
placeholder;

if (isInputSupported && isTextareaSupported) {
Expand All @@ -20,14 +22,40 @@
return this
.filter((isInputSupported ? 'textarea' : ':input') + '[placeholder]')
.not('.placeholder')
.bind('focus.placeholder', clearPlaceholder)
.bind('blur.placeholder', setPlaceholder)
.bind({
'focus.placeholder': clearPlaceholder,
'blur.placeholder': setPlaceholder
})
.trigger('blur.placeholder').end();
};

placeholder.input = isInputSupported;
placeholder.textarea = isTextareaSupported;

hooks = {
'get': function(element) {
var $element = $(element);
return $element.hasClass('placeholder') ? '' : element.value;
},
'set': function(element, value) {
var $element = $(element);
if (value == '') {
element.value = value;
// We can’t use `triggerHandler` here because of dummy text/password inputs :(
setPlaceholder.call(element);
} else if ($element.hasClass('placeholder')) {
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

This comment has been minimized.

Copy link
@devongovett

devongovett Feb 10, 2012

Actually, you can and it will set the element.value property by default which means you could remove the else block above as well. :)

This comment has been minimized.

Copy link
@mathiasbynens

mathiasbynens Feb 10, 2012

Author Owner

Yeah, I wanted to avoid that, as the value must not be set on element at all times… In the case of a text input clone of a password inputs, the real password element should get the value.

I suppose I could do return clearPlaceholder… to get rid of the else but it seems like jQuery would be doing more work internally than simply setting the value property. By the time we end up here, I’ve already checked if value == ''; no need for jQuery to repeat that check.

return $element;
}
};

isInputSupported || (valHooks.input = hooks);

This comment has been minimized.

Copy link
@devongovett

devongovett Feb 10, 2012

So what about breaking code that uses valHooks.checkbox for instance as you told me?

This comment has been minimized.

Copy link
@mathiasbynens

mathiasbynens Feb 10, 2012

Author Owner

@devongovett I decided to just wait and see what breaks. Also, I filed a bug with jQuery and it looks like it will be fixed in a future release.

(It’s easy enough to use $(inputCheckbox).prop('checked', true); instead of .val() for those few edge cases.)

Thanks again for the suggestion!

This comment has been minimized.

Copy link
@devongovett

devongovett Feb 10, 2012

Good man, thanks! :)

isTextareaSupported || (valHooks.textarea = hooks);

$(function() {
// Look for forms
$(document).delegate('form', 'submit.placeholder', function() {
Expand All @@ -41,7 +69,9 @@

// Clear placeholder values upon page reload
$(window).bind('unload.placeholder', function() {
$('.placeholder').val('');
$('.placeholder').each(function() {
this.value = '';
});
});

}
Expand All @@ -58,24 +88,32 @@
return newAttrs;
}

function clearPlaceholder() {
var $input = $(this);
if ($input.val() === $input.attr('placeholder') && $input.hasClass('placeholder')) {
function clearPlaceholder(event, value) {
var input = this,
$input = $(input);
if (input.value == $input.attr('placeholder') && $input.hasClass('placeholder')) {
if ($input.data('placeholder-password')) {
$input.hide().next().show().focus().attr('id', $input.removeAttr('id').data('placeholder-id'));
$input = $input.hide().next().show().attr('id', $input.removeAttr('id').data('placeholder-id'));
// If `clearPlaceholder` was called from `$.valHooks.input.set`
if (event === true) {
return $input[0].value = value;
}
$input.focus();
} else {
$input.val('').removeClass('placeholder');
input.value = '';
$input.removeClass('placeholder');
}
}
}

function setPlaceholder() {
var $replacement,
$input = $(this),
input = this,
$input = $(input),
$origInput = $input,
id = this.id;
if ($input.val() === '') {
if ($input.is(':password')) {
if (input.value == '') {
if (input.type == 'password') {
if (!$input.data('placeholder-textinput')) {
try {
$replacement = $input.clone().attr({ 'type': 'text' });
Expand All @@ -84,18 +122,23 @@
}
$replacement
.removeAttr('name')
// We could just use the `.data(obj)` syntax here, but that wouldn’t work in pre-1.4.3 jQueries
.data('placeholder-password', true)
.data('placeholder-id', id)
.data({
'placeholder-password': true,
'placeholder-id': id
})
.bind('focus.placeholder', clearPlaceholder);
$input
.data('placeholder-textinput', $replacement)
.data('placeholder-id', id)
.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').val($input.attr('placeholder'));
$input.addClass('placeholder');
$input[0].value = $input.attr('placeholder');
} else {
$input.removeClass('placeholder');
}
Expand Down
4 changes: 2 additions & 2 deletions jquery.placeholder.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit b7a4199

Please sign in to comment.