9

Recently I have been looking at jquery/javascript solutions to emulating the placeholder attribute, but none of them seem to have it right. Common issues are:

  • Native browser support is not used first
  • The form may actually post the value of the placeholder text
  • Inability to recognize between placeholder text and user input if the values are identical (Hey, my email IS user@example.com!! Why won't it submit? Hey I wanted to search for the string "Search" but it won't let me!)
  • Difficult to style or distinguish between regular text and placeholder text
  • Obtrusive implementation (almost always. I don't want to add 2 divs per input and a stylesheet to do this)
  • Just doesn't work. At least 2 of those on the jQuery website.

I have played around a bit trying to get this to work properly (taking a few hints from some of the code I have seen already), but it still needs work. Particularly, the form may post the value of the placeholder. Comments and mods to the jsfiddle are welcome. (Note: Demo must be viewed in a browser without placeholder support) Most code I've seen takes the value of the placeholder and stuffs it into the input itself which leads to this problem, I feel like there must be a better way.

Is there a nice clean solution that actually works?

EDIT: I want to stress this: it should behave the way you see it in browsers that natively support it as much as possible, and be as unobtrusive as possible, as demonstrated in my current code, which does not require anything other than including the script and using the placeholder as you normally would for browsers that support it.

UPDATE: @DA's current solution is a couple of bug fixes away from perfect (see comments), would love to see this come together 100% and put all the bad & buggy code on the net to shame.

UPDATE: Got this working with a couple mods to DA's code but it's still not perfect, mostly in regards to dynamically added input fields and existing submit() bindings. Thanks for all the help, I've decided for now that it's not worth it. I know a few people that will definitely use this though. It's a nice trick to have, but to me not worth even a 1% possibility of a form submit doing something its not intended to, or reading user input incorrectly. This minor feature is just not worth the headache, IE and pals can just deal with it, or it can be implemented on a case-by-case basis if it's really needed, like if the client demands it. @DA thanks again, this is the best implementation I've seen.

CONCLUSION: I think the only way to overcome all these issues is something like this:

  • Copy the placeholder value to a new block level element
  • Prepend the new element to the input
  • Calculate the height border and padding of the input and move the new element over it as close as possible to the position that text would occur
  • Use the standard blur/change/focus events to hide the element when the input has a value or is focused

This way you don't have to do anything on submit or deal with any of the other issues that can occur. Sorry no demo yet, I've got to get back to work - but I'll save a final edit for when this comes together.

Wesley Murch
  • 101,186
  • 37
  • 194
  • 228
  • If you really want something that fits all those points, it's easy to write: Simply create this structure: `
    ` and set span content to your 'placeholder' text whenever the input is empty.
    –  Apr 07 '11 at 03:59
  • @cwolves: Something similar to this is starting to look like the best solution now. – Wesley Murch Apr 07 '11 at 16:48
  • One minor issue is that we've found it can be hard to pass the focus from the overlaid text to the input field on some mobile devices. What happens is that you can pass the focus, but the keyboard will not always trigger, since on some devices that isn't controlled by a focus event, but specifically from an actual touch event (which wouldn't happen if you had clicked the text on top, rather than the actual input field). – DA. Apr 07 '11 at 18:17
  • @DA: Hmm.. can the touch event be made to `trigger()` the focus event? Mobile development is a complete unknown to me, so I'm sure you already thought of that. BTW I dropped your code into my CMS to test it, which is where I had issues due to various other submit bindings and "ajax" generated form controls, but the recognition of user input did indeed work. – Wesley Murch Apr 07 '11 at 18:27
  • Well, to be fair, dealing with BlackBerry browsers is like dealing with IE. They just suck, so in some ways, I hate having to accommodate them when there's code that works everywhere else. On the plus side, I *think* most touch devices (windows 7 may be a big exception) will support the HTML5 placeholder properly, so maybe it's not a huge deal. – DA. Apr 07 '11 at 18:55

4 Answers4

4

"Most code I've seen takes the value of the placeholder and stuffs it into the input itself which leads to this problem"

Well, that's pretty much what the placeholder text does behind the scenes (albeit, it's not literally updating the value of the input).

One option is to something akin to this:

http://fuelyourcoding.com/scripts/infield/

The concept there is you move the LABEL with CSS so that's on top of the field and looks like placeholder text. Note that a LABEL isn't necessarily the same as placeholder text. A LABEL is important to have, especially for accessibility, while placeholder text can be thought more as 'help text' in addition to the label.

The other option is what you've been doing, take the content of the placeholder attribute and move it into the value of the input itself via JS.

Where you're getting hung up is you are submitting the form before checking to see if the fake placeholder text is still there.

To get around that, you want to attach an event to the submission of the form that, before submitting the form, will first look at all the input fields, grab their values, compare it to their placeholder attributes, and if it matches, set the value to blank.

UPDATE:

To address the issue you brought up in a comment of the user-inputted value matching the placeholder text, you could do something like this:

$('input[placeholder]').each(function(){

 if($(this).val() === $(this).attr('placeholder')){
      // in the odd situation where the field is prepopulated with
      // a value and the value just happens to match the placeholder,
      // then we need to flag it
      $(this).data('skipCompare','true');
  }    

// move the placeholder text into the field for the rest of the blank inputs
   if($(this).val().length===0){
       $(this).val($(this).attr('placeholder'))
   }

// move the placeholder text into the field for the rest of the blank inputs
  if($(this).val().length===0){
      $(this).val() = $(this).attr('placeholder');
  }


  $(this)
     .focus(function(){
           // remove placeholder text on focus
           if($(this).val()==$(this).attr('placeholder')){
               $(this).val('')
           }
     })
     .blur(function(){
         // flag fields that the user edits
         if( $(this).val().length>0 && $(this).val()==$(this).attr('placeholder')){
             $(this).data('skipCompare','true');
         }
         if ( $(this).val().length==0){
             // put the placeholder text back
             $(this).val($(this).attr('placeholder'));
         }
     })


 })

 $('#submitButton').click(function(){
   $('input[placeholder]').each(function(){
     if( !($(this).data('skipCompare')) && $(this).val() == $(this).attr('placeholder')     ){
         $(this).val('');
     };
     alert($(this).val());
   })
})

It's late and I'm tired so all that might be completely wrong. No warranties given. ;)

DA.
  • 39,848
  • 49
  • 150
  • 213
  • This is pretty nice from a user's perspective, but as-is it messes with your labels which is bad in many ways. I'd like to see a modified version of this that maybe clones the label instead. I'll have to give this a closer look, thanks. – Wesley Murch Apr 07 '11 at 04:06
  • What do you mean 'messes with your labels'? – DA. Apr 07 '11 at 04:11
  • I mean that it removes your ` – Wesley Murch Apr 07 '11 at 04:13
  • Sorry, maybe I confused things. My answer is talking about two different things. One is to use a label tag and position it on top of the field. It's still the label tag, you're just moving it via CSS. The other is pretty much what you've been building, where you use JS to move a placeholder attribute value into the input value. I think both have merits and are useful in slightly different contexts. – DA. Apr 07 '11 at 04:15
  • The goal is to emulate native browser implementation (as you would see in FF4 or Chrome, for instance), which this code does not do very well. – Wesley Murch Apr 07 '11 at 04:19
  • We're going to need more feedback from you than the 'does not do very well'. What, specifically, is it not doing very well? I took a stab at a full chunk of jQuery to make this work. Let me know what you think it might be missing or failing at. Would love to tweak it as needed. – DA. Apr 07 '11 at 04:32
  • Well for one thing, what if my label is "Please enter a date" and I want my placeholder to say "YYYY-MM-DD", this solution does not work. – Wesley Murch Apr 07 '11 at 04:35
  • Well, first of all, that was jQuery, so you need to have jsFiddle load jQuery. But...I had some typos too. Here's the working code. I'll update my answer with it: http://jsfiddle.net/65sNw/1/ – DA. Apr 07 '11 at 05:26
  • Aha, my mistake I forgot to load jQuery! Saw your new example, still suffers from inability to recognize user text vs placeholder text (type the placeholder text into an input and lose focus, input will clear). Otherwise this is working pretty awesome. +1. Sorry to be a hard-ass about this, I spent way too much time on this today sifting through failure code, and working on it myself. I still believe that a user entering text that matches the placeholder is a valid concern, albeit a small one, but I'd prefer to avoid it altogether. This is the best answer so far, thank you very much. – Wesley Murch Apr 07 '11 at 05:53
  • @DA: Just had a lightbulb go off for the anal-retentive issue of user text vs placeholder: Appending the placeholder value with a bunch of spaces. It's hackish, but definitely works and the spaces wont be visible in the placeholder. User searched for "Search"? Yeah maybe. User searched for "Search     ", no way (formatting removed my extra spaces). Will update tomorrow, so tired... – Wesley Murch Apr 07 '11 at 06:18
  • @Mad...the form seems to be checking if the user entered the placeholder text. Enter the text in the first field and hit the button, you will see in the alert that it grabs the text, even though it matches the placeholder. The click event was just for demo...though I do believe jQuery will call that event regardless of how the form is submitted (I'd have to double check on that). Not sure if extra spaces will work, but it's an idea (more than one space in HTML is ignored...that may be true for attribute values as well) – DA. Apr 07 '11 at 06:35
  • Any reply to this answer will ping me. Thanks for the update! Heads up, your final solution will give you some headaches on mobile devices. We ran into that very issue on blackberries and had to scrap the idea of positioning the text over the field. Do look at my example again, it does what you want (I think) in that it lets a user type in the exact same text as the placeholder and still submit it. – DA. Apr 07 '11 at 18:16
  • You can't access password field data via JS AFAIK. – DA. Sep 17 '12 at 00:52
  • submit worked for me, I've just attached it to all the $('input[type=submit]') – E Ciotti Mar 26 '13 at 14:40
2

jquery plugin I wrote a while ago:

(function(){
    $.fn.inactiveText = function(inactiveClass, defaultValue){
        var a, b;
        this
            .data    ('defaultValue', defaultValue)
            .addClass('inactive')
            .focus(a=function(){
                var $this = $(this);
                if($this.hasClass(inactiveClass)){
                    (valFn.apply($this) == defaultValue) && $this.val('');
                    (valFn.apply($this) != defaultValue) && $this.removeClass(inactiveClass);
                }
    //            $this.hasClass(inactiveClass) && $this.valueIs(defaultValue).val('').end().valueIsNot(defaultValue).removeClass(inactiveClass);
            })
            .blur(b=function(){
                var $this = $(this);
                this.value || $this.addClass(inactiveClass).val(defaultValue);
            });
        this.each(a);
        this.each(b);

        this.setDefaultValue = function(d){
            this.each(a);
            defaultValue = d;
            this.each(b);
        };

        return this;
    };

    var valFn = $.fn.val;

    $.fn.val = function(v){
        if(typeof(v) == 'undefined'){
            var val = valFn.apply(this, arguments);
            return val == this.data('defaultValue') ? '' : val;
        }

        return valFn.apply(this, arguments);
    };
})();

Define a css class for the inactive style (e.g. grey text) and you're good to go.

updated fiddle: http://jsfiddle.net/cwolves/At8cu/1/

  • Doesn't work right, try populating one of the fields, blanking it out, and losing focus. Use "Your name" as the default `value` for the name field, clear the name field manually and lose focus, it copies the email placeholder to the name field. I posted a fiddle with this but it didn't save my update, still learning how to fiddle :) – Wesley Murch Apr 07 '11 at 03:46
  • That's just because I didn't change the selector, it's still using `$('[placeholder]')`. Change it to `$('[name=email]')` and it'll be fine, sorry :) –  Apr 07 '11 at 03:49
  • Then this falls into the *obtrusive* category if I have to define it for every field. Close but no cigar. – Wesley Murch Apr 07 '11 at 03:51
  • then modify it to read `.attr('placeholder')`, not that hard :) You have to define it for every field anyway, I just chose to do it in JS as opposed to inline HTML, mostly because the project I wrote this for had JS language toggles. –  Apr 07 '11 at 03:53
  • Sorry, I'm not going to do everything for you! Besides, you'd probably prefer the method I described in the reply to your original question and I'm not writing that :) –  Apr 07 '11 at 04:00
1

Assume jQuery is defined.

$(document).ready(function() {
    // Use native functionality if possible
    if (document.createElement('input').placeholder !== undefined) return;
    // Fallback code
    $('form').each(function(i, form) {
        var submitted = false;
        $(form).find('input[placeholder]').each(function(j, input) {
            addPlaceholder.call(input);
            $(input)
                .focus(removePlaceholder)
                .blur(addPlaceholder)
                ;
        });
        $(form).submit(function() {
            submitted = true;
            $(this).find('input[placeholder]').each(function(j, input) {
                removePlaceholder.call(input);
            });
        });
        function addPlaceholder() {
            if (!submitted && '' === $(this).val()) {
                $(this).val($(this).attr('placeholder')).addClass('placeholder');
            }
        }
        function removePlaceholder() {
            if ( $(this).hasClass('placeholder') &&
                 $(this).attr('placeholder') === $(this).val()
                 ) {
                $(this).val('').removeClass('placeholder');
            }
        }
    });
});

Style your placeholder with CSS to make it look the same in all browsers:

            :-moz-placeholder { color: #bbb; opacity: 1; }
           ::-moz-placeholder { color: #bbb; opacity: 1; }
       :-ms-input-placeholder { color: #bbb; }
  ::-webkit-input-placeholder { color: #bbb; }
                 .placeholder { color: #bbb; }
blah
  • 11
  • 2
0

Tried these links?


HTML5 Forms Placeholder Fallback
jQuery Placeholder Text
Placeholder Attribute Support in ALL browsers

naveen
  • 53,448
  • 46
  • 161
  • 251
  • I just glanced at all those briefly, they will all (I believe) fail on field.val(), giving you the placeholder text instead of ''. –  Apr 07 '11 at 03:43
  • the placeholder attribute can be there regardless of whether or not the browser does anything with it. As such, when you run your validation, check the value of the field against the value of that field's placeholder attribute. If they match, then you can assume the field is "blank". – DA. Apr 07 '11 at 03:46
  • I checked these links and they all fail on some point. @DA: Not strictly speaking, see bullet point 3 in my question, and this approach also falls into the *obtrusive* category. – Wesley Murch Apr 07 '11 at 03:54
  • That seems like an extremely rare edge case that your Placeholder text would also be user-inputted text. That said, you could get around that. Onblur, check the value of the field. If there is one, then flag the field as 'user data' and then ignore the comparison to the placeholder upon submission. – DA. Apr 07 '11 at 03:58
  • @DA, I already have this under control in the fiddle I posted in my question, please have a look. – Wesley Murch Apr 07 '11 at 04:01
  • In your script, it looks like you are assigning an EMPTY data only to fields that had a default value and that's all you are checking for when submitting. You also have to compare all the placeholders with the input values. – DA. Apr 07 '11 at 04:10
  • No, only at first, then it uses `change()` to assign the data. It does clear the fields correctly as I'm asking it to, but still sends post data, but I have not tested it without a js submit yet. This is the only point of failure in my code that I am aware of, but of course its a very important one! – Wesley Murch Apr 07 '11 at 04:28