Gravity Forms is an amazing premium product for creating all kinds of forms, offering great flexibility to a WordPress developer. If you have to build a WordPress theme based on somebody else’s HTML, though, like for example an HTML Template from ThemeForest, you will probably find yourself trapped between markups: Gravity Forms has its own, specific markup which rarely (actually, “never” would probably be more accurate) matches the one from your templates.

Sure, you can go a long way with CSS and there are filters which allow you to modify the fields’ output, but if you want to completely change the form’s structure, things can quickly get frustrating. That’s because there is no filter for modifying the core structure of the form, which, if you strip it down, for convenience’s sake, looks like this:

<div class="gform_wrapper" id="gform_wrapper_1">
    <form id="gform_1">
        <div class="gform_body">
            <ul id="gform_fields_1" class="gform_fields">
                <li id="field_1_1" class="gfield">
                    <label class="gfield_label" for="input_1_1">Some text</label>
                    <div class="ginput_container">
                        <input name="input_1" id="input_1_1" type="text" value="" class="text medium">
                    </div>
                </li>
            </ul>
        </div>
        <div class="gform_footer">
            <input type="submit" id="gform_submit_button_1" class="gform_button button" value="Submit">
        </div>
    </form>
</div>

There is a wrapper  div containing the entire form element and then a  div.gform_body and a  div.gform_footer, containing the form’s fields and the submit button respectively. Then, inside the form’s body you can see an  <ul> element, with one  <li> for each field and then, again, one more  <div> wrapper for the input. There is no way that you are lucky enough to find an HTML template matching that markup.

So, assuming that you don’t want to rewrite the entire CSS for the form, which defeats the benefits of using ready-made HTML anyway, and str_replace() or preg_replace() can only get you so far, what options are left to fully customize your form’s markup? After some research and experimentation, what seemed like the most sane approach was the use of DOMDocument. DOMDocument is a native PHP class which allows you to manipulate HTML using predictable functions. Here is how we can use it to manipulate Gravity Form’s output:

First of all, we need to use gravity_form function call to get the form’s original markup and store it into a variable:

$form = gravity_form( $form_id, false, false, null, null, true, 1, false );

The most important parameters here are the first and the last: $form_id is our form’s ID, while the last parameter refers to the function’s “echo” option. It is set to “false” because we want to store the content to a variable instead of echoing it. The rest of the parameters can be changed according to our project’s needs.

Then, we need to create a new DOM object and load the form’s HTML into it:

$dom = new domDocument();
$dom->loadHTML( '<?xml encoding="utf-8" ?>' . $form );

To clean-up white spaces, we can also add:

$dom->preserveWhiteSpace = false;

Now that we have the $dom object, containing our entire form’s markup, we can go and get our actual  <form> object and store it in a variable. We know that the element has a specific id, so we grab it like that:

$gform = $dom->getElementById( 'gform_' . $form_id );

Then, we need to get the form’s body and footer contents and store them in variables too. The tricky part here is that we don’t actually want the containers but only their contents. To do so, we need to use the DOMXPath class which will allow us to run some regular expressions:

$xpath = new DomXPath( $dom );
$gform_ul             = $xpath->query( '//ul[@id="gform_fields_' . $form_id . '"]/*' );
$gform_footer_content = $xpath->query( '//div[contains(@class, "gform_footer")]/*' );

Similarly, we will also need the actual containers of  div.gform_body and  div.gform_footer and store them in different variables for later use. Actually, we need them stored in order to be able to remove them from the DOM. Since those elements don’t have an ID, we will again take advantage of DOMXPath and some regular expressions, to select them by their class names:

$gform_body   = $xpath->query( '//div[contains(@class, "gform_body")]' )->item( 0 );
$gform_footer = $xpath->query( '//div[contains(@class, "gform_footer")]' )->item( 0 );

The reason we stored all those variables was because we need to keep the contents of the containers but remove the containers themselves, moving that way the contents one level up. So, starting with the div.gform_body container, first we remove it from the form:

$gform->removeChild( $gform_body );

and then we loop over its contents, which we stored earlier, and append each one of its nodes in the form:

foreach ( $gform_ul as $node ) {
  $gform->appendChild( $node );
}

Now we do the same for the div.gform_footer:

$gform->removeChild( $gform_footer );
foreach ( $gform_footer_content as $node ) {
  $gform->appendChild( $node );
}

And we are almost done. The last step to make it work is to echo our new $dom like so:

echo $dom->saveHTML();

To sum things up, here is the full code of all the above:

// Get the form's HMTL
$form = gravity_form( $form_id, false, false, null, null, true, 1, false );
// Create a new dom object.
$dom = new domDocument();
// Load the form's html into the object.
$dom->loadHTML( '<?xml encoding="utf-8" ?>' . $form );
// Discard white space.
$dom->preserveWhiteSpace = false;
// Get the main form Element by ID.
$gform = $dom->getElementById( 'gform_' . $form_id );
// Use DomXPath to find elements by regular expressions (classname and id).
$xpath = new DomXPath( $dom );
// Get the form's body and footer contents and store them in variables.
$gform_ul             = $xpath->query( '//ul[@id="gform_fields_' . $form_id . '"]/*' );
$gform_footer_content = $xpath->query( '//div[contains(@class, "gform_footer")]/*' );
// Get the form's body and footer nodes in order to modify them.
$gform_body   = $xpath->query( '//div[contains(@class, "gform_body")]' )->item( 0 );
$gform_footer = $xpath->query( '//div[contains(@class, "gform_footer")]' )->item( 0 );
// Remove the body container and append the contents that we saved earlier.
$gform->removeChild( $gform_body );
foreach ( $gform_ul as $node ) {
    $gform->appendChild( $node );
}
// Remove the footer container.
$gform->removeChild( $gform_footer );
foreach ( $gform_footer_content as $node ) {
  $gform->appendChild( $node );
}
echo $dom->saveHTML();

If you would like to have it in a more organized way, here it is in the form of a standalone class with a few extra goodies, as part of my DareDev mu-plugin.

If everything went OK, our form’s new structure should look like that (plus some extra attributes, a few hidden fields and some javascript that Gravity Forms adds for validation, which don’t affect the styling and I’ve skipped to make the structure more readable):

<div class="gform_wrapper" id="gform_wrapper_1">
    <form id="gform_1">
        <li id="field_1_1" class="gfield">
            <label class="gfield_label" for="input_1_1">Some text</label>
            <div class="ginput_container">
                <input name="input_1" id="input_1_1" type="text" value="" class="text medium">
            </div>
        </li>
        <input type="submit" id="gform_submit_button_1" class="gform_button button" value="Submit">
    </form>
</div>

The <div> containers are gone, the <ul> element is gone too and it definitely is much cleaner compared to the original. Of course, we still have the <li> elements that have to go and we would probably want to wrap the fields with some extra markup, based on our HTML template’s structure.

This is something that can be done using native Gravity Form’s filters, though. More specifically, we can use gform_field_container to remove the <li></li> markup from the inputs and maybe remove the labels as well. gform_field_input will allow us to add extra classes or even perform more complicated manipulations to the fields and with gform_submit_button we can modify the form’s submit button.

Combining all the above you will hopefully be able to fully modify the markup of your Gravity Form, with a relatively small effort. Of course, “relatively” is relative (see what I did here?) and depending on the complexity of the HTML the task might become harder. So, if you have another, better or just different, way to deal with the issue, I would be glad to hear from you.

3 thoughts on “Custom HTML markup with Gravity Forms

  1. This is great, thank you for this.
    1. Where do you put this code in your project? Can you just wrap it in a function and use one of the existing GF hooks?
    2. I don’t want to remove the divs, I want to add classes into them for my grid. Could you post an example of manipulating existing markup within those variables?

  2. 1. Yes, you can wrap it in a function and put it in your functions.php.
    2. Actually, the Form.class.php has an option to keep the footer wrap and set your own class. You can apply the same logic to the body as well, following the comments.

  3. Seems to me that quite a lot of Gravity Forms-related code is dependent on some of the key elements that you’ve just removed. Remembering that forms are structured on purpose, I hesitate to remove the semantic structure that helps define the form. Display info (CSS) always ought to be a separate question.

    A goal was defined of “completely changing the form’s structure”, yet did this example actually do that?
    * Some wrappers were removed… valuable wrappers at that, which make it simple to have custom CSS specific to this form as opposed to the rest of the site.

    What has that actually accomplished? It’s been tweaked for:
    * MORE compatibility with something that already exists… and
    * LESS compatibility with everything in the gf universe.

    I don’t see the big win here.

Leave a Reply

Your email address will not be published. Required fields are marked *