Skip to content
Browse files

Make `.val()` work as expected

* 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...
1 parent 06d8e67 commit b7a4199af079ae0210dca5db36d3fe64b9508ff4 @mathiasbynens committed Feb 10, 2012
Showing with 76 additions and 27 deletions.
  1. +11 −5 README.md
  2. +2 −2 demo.html
  3. +61 −18 jquery.placeholder.js
  4. +2 −2 jquery.placeholder.min.js
View
16 README.md
@@ -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/">
@@ -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">
```
View
4 demo.html
@@ -29,11 +29,11 @@
<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
View
79 jquery.placeholder.js
@@ -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) {
@@ -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
@devongovett
devongovett added a note 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. :)

@mathiasbynens
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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ return $element;
+ }
+ };
+
+ isInputSupported || (valHooks.input = hooks);
@devongovett
devongovett added a note Feb 10, 2012

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

@mathiasbynens
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!

@devongovett
devongovett added a note Feb 10, 2012

Good man, thanks! :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ isTextareaSupported || (valHooks.textarea = hooks);
+
$(function() {
// Look for forms
$(document).delegate('form', 'submit.placeholder', function() {
@@ -41,7 +69,9 @@
// Clear placeholder values upon page reload
$(window).bind('unload.placeholder', function() {
- $('.placeholder').val('');
+ $('.placeholder').each(function() {
+ this.value = '';
+ });
});
}
@@ -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' });
@@ -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');
}
View
4 jquery.placeholder.min.js
@@ -1,2 +1,2 @@
-/*! http://mths.be/placeholder v1.8.7 by @mathias */
-(function(f,h,c){var a='placeholder' in h.createElement('input'),d='placeholder' in h.createElement('textarea'),i=c.fn,j;if(a&&d){j=i.placeholder=function(){return this};j.input=j.textarea=true}else{j=i.placeholder=function(){return this.filter((a?'textarea':':input')+'[placeholder]').not('.placeholder').bind('focus.placeholder',b).bind('blur.placeholder',e).trigger('blur.placeholder').end()};j.input=a;j.textarea=d;c(function(){c(h).delegate('form','submit.placeholder',function(){var k=c('.placeholder',this).each(b);setTimeout(function(){k.each(e)},10)})});c(f).bind('unload.placeholder',function(){c('.placeholder').val('')})}function g(l){var k={},m=/^jQuery\d+$/;c.each(l.attributes,function(o,n){if(n.specified&&!m.test(n.name)){k[n.name]=n.value}});return k}function b(){var k=c(this);if(k.val()===k.attr('placeholder')&&k.hasClass('placeholder')){if(k.data('placeholder-password')){k.hide().next().show().focus().attr('id',k.removeAttr('id').data('placeholder-id'))}else{k.val('').removeClass('placeholder')}}}function e(){var o,n=c(this),k=n,m=this.id;if(n.val()===''){if(n.is(':password')){if(!n.data('placeholder-textinput')){try{o=n.clone().attr({type:'text'})}catch(l){o=c('<input>').attr(c.extend(g(this),{type:'text'}))}o.removeAttr('name').data('placeholder-password',true).data('placeholder-id',m).bind('focus.placeholder',b);n.data('placeholder-textinput',o).data('placeholder-id',m).before(o)}n=n.removeAttr('id').hide().prev().attr('id',m).show()}n.addClass('placeholder').val(n.attr('placeholder'))}else{n.removeClass('placeholder')}}}(this,document,jQuery));
+/*! http://mths.be/placeholder v2.0.0 by @mathias */
+;(function(f,h,$){var a="placeholder" in h.createElement("input"),d="placeholder" in h.createElement("textarea"),i=$.fn,c=$.valHooks,k,j;if(a&&d){j=i.placeholder=function(){return this};j.input=j.textarea=true}else{j=i.placeholder=function(){return this.filter((a?"textarea":":input")+"[placeholder]").not(".placeholder").bind({"focus.placeholder":b,"blur.placeholder":e}).trigger("blur.placeholder").end()};j.input=a;j.textarea=d;k={get:function(m){var l=$(m);return l.hasClass("placeholder")?"":m.value},set:function(m,n){var l=$(m);if(n==""){m.value=n;e.call(m)}else{if(l.hasClass("placeholder")){b.call(m,true,n)||(m.value=n)}else{m.value=n}}return l}};a||(c.input=k);d||(c.textarea=k);$(function(){$(h).delegate("form","submit.placeholder",function(){var l=$(".placeholder",this).each(b);setTimeout(function(){l.each(e)},10)})});$(f).bind("unload.placeholder",function(){$(".placeholder").each(function(){this.value=""})})}function g(m){var l={},n=/^jQuery\d+$/;$.each(m.attributes,function(p,o){if(o.specified&&!n.test(o.name)){l[o.name]=o.value}});return l}function b(m,n){var l=this,o=$(l);if(l.value==o.attr("placeholder")&&o.hasClass("placeholder")){if(o.data("placeholder-password")){o=o.hide().next().show().attr("id",o.removeAttr("id").data("placeholder-id"));if(m===true){return o[0].value=n}o.focus()}else{l.value="";o.removeClass("placeholder")}}}function e(){var q,l=this,p=$(l),m=p,o=this.id;if(l.value==""){if(l.type=="password"){if(!p.data("placeholder-textinput")){try{q=p.clone().attr({type:"text"})}catch(n){q=$("<input>").attr($.extend(g(this),{type:"text"}))}q.removeAttr("name").data({"placeholder-password":true,"placeholder-id":o}).bind("focus.placeholder",b);p.data({"placeholder-textinput":q,"placeholder-id":o}).before(q)}p=p.removeAttr("id").hide().prev().attr("id",o).show()}p.addClass("placeholder");p[0].value=p.attr("placeholder")}else{p.removeClass("placeholder")}}}(this,document,jQuery));

0 comments on commit b7a4199

Please sign in to comment.
Something went wrong with that request. Please try again.