Django HTML5 input placeholders

HTML5 has a bunch of nifty progressive enhancements to forms, one of which is placeholder text. Here is an example, along with the required code.

<form style="float: right; margin: 1.75em;">
   <input size="38" placeholder="Your browser supports placeholder text">
Placeholder text is displayed inside the input field as long as the field is empty and not focused. As soon you click on (or tab to) the input field, the placeholder text disappears. - Mark Pilgrim

I wanted to leverage this feature using Django's built-in form generation. Ie, I wanted to be able to call form.as_table, and have placeholder values pulled either from the model field's help_text, or from the form definition.

My first problem was that help_text currently just dumps out into the form body. I needed a way to disable, or at least hide, those elements. There is an open feature request to wrap those elements in a class, so that they can be easily styled/hidden. While that feature will make it into Django 1.3, here is a solution in the meantime.

def wrap_helptext_as_table(self):
    return self._html_output(
        u'<tr><td colspan="2">%s</td></tr>',
        u'<div class="help_text">%s</div>',

class Html5Form(Form):

    def as_table(self):
        return wrap_helptext_as_table(self)

class Html5ModelForm(ModelForm):

    def as_table(self):
        return wrap_helptext_as_table(self)

# .help_text { display: none; } will now hide help_text elements

These two subclasses, one for regular forms and one for model forms, comprised the smallest change to the base classes that I could come up with to wrap help_text in a div with a css class. On to inserting the HTML5 placeholder attribute.

class Html5ModelForm(ModelForm):

    def as_table(self):
        return wrap_helptext_as_table(self)

    def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
                 initial=None, error_class=ErrorList, label_suffix=':',
                 empty_permitted=False, instance=None):

        super(Html5ModelForm, self).__init__(data, files, auto_id, prefix,
            initial, error_class, label_suffix,
            empty_permitted, instance)

        # create an HTML5 placeholder attribute based on the field help_text
        for field_name in self.fields:
            field = self.fields.get(field_name)
            if field:
                if type(field.widget) == TextInput:
                    field.widget.attrs["placeholder"] = field.help_text

That's a complete solution for model forms. For regular forms, all you have to do is the following in the form definition.

class MyForm(Form):
    name = forms.CharField(widget=forms.TextInput({ "placeholder": "Joe Recruiter" }))

I'm currently working at NerdWallet, a startup in San Francisco trying to bring clarity to all of life's financial decisions. We're hiring like crazy. Hit me up on Twitter, I would love to talk.

Follow @chase_seibert on Twitter