32

I use Contact Form 7 in my WordPress theme.

It is currently returning span and input:

<span class="wpcf7-form-control-wrap name">
  <input type="text" name="name" class="wpcf7-form-control wpcf7-text wpcf7-validates-as-required form-control" id="name">
</span>

But I need only input:

<input type="text" name="name" class="wpcf7-form-control wpcf7-text wpcf7-validates-as-required form-control" id="name">

How can I remove the span wrapper?

Matthew Beckman
  • 1,702
  • 2
  • 16
  • 28
SVE
  • 1,555
  • 4
  • 30
  • 57

8 Answers8

46

I faced the same problem and finally ending by using the wpcf7_form_elements filter to remove the <span> tag with a regex. You can for example copy-paste this code in your functions.phpfile. Here I pass an an anonymous function as a callback, so be sure to have PHP >= 5.3.

add_filter('wpcf7_form_elements', function($content) {
    $content = preg_replace('/<(span).*?class="\s*(?:.*\s)?wpcf7-form-control-wrap(?:\s[^"]+)?\s*"[^\>]*>(.*)<\/\1>/i', '\2', $content);

    return $content;
});
Guicara
  • 1,668
  • 2
  • 20
  • 34
  • 3
    it is working, but it removed the error message too.... I am not familiar with regex. – jho1086 Mar 09 '17 at 19:13
  • 1
    @jho1086, this is not a problem with the regex, but the JS in WPCF7. It probably uses the span (that this filter removes) as the target container when adding the error message span. Since the span doesn't exist it doesn't know where to add the error span. – Punchlinern Jun 28 '17 at 10:37
  • 1
    @Punchlinern so is there a known solution for adding the error span properly using this regex ? – Marine Le Borgne Jul 27 '17 at 16:27
  • 1
    Hi. Was anyone able to find a solution for the error spans also getting removed? – NJT Jan 08 '19 at 04:33
12

I tried similar stuff the other day and many people mentioned, that Regex is not the proper way to alter HTML/remove tags etc and it sounds logic.

So i went for DOMDocument and came up with following solution:

add_filter('wpcf7_form_elements', function( $content ) {
  $dom = new DOMDocument();
  $dom->preserveWhiteSpace = false;
  $dom->loadHTML(mb_convert_encoding($content, 'HTML-ENTITIES', 'UTF-8'), LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);

  $xpath = new DomXPath($dom);
  $spans = $xpath->query("//span[contains(@class, 'wpcf7-form-control-wrap')]" );

  foreach ( $spans as $span ) :
    $children = $span->firstChild;
    $span->parentNode->replaceChild( $children, $span );
  endforeach;

  return $dom->saveHTML();
});

EDIT: I now also added some html/text to my form, and the first heading element was not wrapping correctly after i used the DOMDocument class. It started in the first line and ended at the very end of the form. so i wrapped my entire form in a div, what made the markup return properly again.

honk31
  • 3,895
  • 3
  • 31
  • 30
  • This is working fine! I just mention changing line 7 if you want to still support Honeypot for Contact Form 7: `$spans = $xpath->query( "//span[contains(@class, 'wpcf7-form-control-wrap') and not(@style)]" );` – KittMedia Mar 06 '22 at 19:50
8

As of WPCF7 version 4.9, adapting the answer above so as to allow for error messaging to not be lost:

First of all, in the editor in the CMS, wrap your input field and any other elements that you want grouped, for example:

<span class="wpcf7-form-control-wrap your-name">{field codes, etc, here}</span>

Note that you'll need to use the class "wpcf7-form-control-wrap" and a class that matches your field name.

Next, use this regex code in your functions.php. It may need adapting for your specific needs

add_filter('wpcf7_form_elements', function($content) {
    preg_match_all('/<(span).*?class="\s*(?:.*\s)?wpcf7-form-control-wrap(?:\s[^"]+)?\s*"[^\>]*>(.*)<\/\1>/i', $content,$matches);

    foreach($matches[2] as $match):
        $content = str_replace(trim($match),trim(preg_replace('/<(span).*?class="\s*(?:.*\s)?wpcf7-form-control-wrap(?:\s[^"]+)?\s*"[^\>]*>(.*)<\/\1>/i', '\2', $match)),$content);
    endforeach;
    return $content;
});

This will strip the span tag around the input field while leaving your new span tag intact. The effect is to essentially move the span tag from around only the input field, to being around whatever elements you wish to wrap.

The reason for this is that the code for form submission is unfortunately very hard coded. In order to have complete freedom over the structure of your HTML you would need to either:

  1. Change the code in rest-api.php around line 295 to change the "into" value to something less specific. Naturally this isn't a recommended route though the one that gives you complete freedom to structure your content as you wish. It'll be overwritten with plugin updates.

    foreach ( (array) $result['invalid_fields'] as $name => $field ) {
        $invalid_fields[] = array(
            'into' => 'span.wpcf7-form-control-wrap.'
                . sanitize_html_class( $name ),
            'message' => $field['reason'],
            'idref' => $field['idref'],
        );
    }
    
  2. Tap into the wpcf7:invalid event and run your own validation code on the result. Needless to say this is duplicating a lot of the work the plugin already does for you, when accepting (for now) to use a span tag with the "wpcf7-form-control-wrap" class in the manner described above retains all functionality of the plugin while undoing one of the most annoying hard codings of the plugin.

niaccurshi
  • 1,265
  • 9
  • 9
7

You can remove the span wrapper using jQuery:

$("#name").unwrap();

It will remove input's parent element, so in this case it will remove the span. Please note that after removing the span, some Contact Form 7 features may not work correctly. For example validation may not work.

$("button").click(function(){
  $("#name").unwrap();
});
span {
  background-color: #333;
  padding: 20px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<span class="wpcf7-form-control-wrap name">
  <input type="text" name="name" value="" size="40" class="wpcf7-form-control wpcf7-text wpcf7-validates-as-required form-control" id="name" aria-required="true" aria-invalid="false">
</span>

<button>Remove span</button>
Pawel Janicki
  • 425
  • 3
  • 8
1

Maybe it's old, but still actual problem. Here is the code to output "raw" checkbox|radio without span with id attribute:

add_filter('wpcf7_form_elements', 'contact_form_remove_checkbox_wrapping' );

function contact_form_remove_checkbox_wrapping( $content ) {
    preg_match_all('/<span class="wpcf7-form-control-wrap[a-zA-Z ]*"><span class="wpcf7-form-control wpcf7-[checkbox|radio]+" id="([^\"]+)"><span class="[^\"]+">(<input type="[checkbox|radio]+" name="[^\"]+" value="[^\"]*" \/>)<span class="wpcf7-list-item-label">[^\"]+<\/span><\/span><\/span><\/span>/i', $content, $out );
    if ( !empty( $out[0] ) ) {
        $count = count( $out[1] );
        for ( $i = 0; $i<$count; $i++ ) {
            $out[2][ $i ] = str_replace(' value=', 'id="' . $out[1][ $i ] . '" value=', $out[2][$i] );
        }

        return str_replace( $out[0], $out[2], $content );
    }

    return $content;
}
user3013494
  • 111
  • 9
1

I came up with the simple script that adds class to this span when the input is focused(I wanted to use input:focus + label, but this span was breaking this logic);

/*contact form inputs*/
  var contactFormInputs = document.querySelectorAll('.wpcf7-form-control'); // or your custom input class
  contactFormInputs.forEach(function(element) {
    element.addEventListener('focus', function(event) {
      event.target.parentElement.classList.add("focused");
    });
    element.addEventListener('blur', function(event) {
      event.target.parentElement.classList.remove("focused");
    });
    element.addEventListener('change', function(event) {
     event.target.value
        ? event.target.parentElement.classList.add("valid")
        : event.target.parentElement.classList.remove("valid")
    });
  });

So now, I can use

.wpcf7-form-control-wrap.focused, .wpcf7-form-control-wrap.valid {
  ~ .ui-block-label {
    top: -20px;
    font-size: 14px;
    color: $yellowDark;
  }
}

in my SCSS code.

Artem Luzhanovskyi
  • 1,767
  • 1
  • 11
  • 8
1

This example works for me and shows how to remove <span class="wpcf7-form-control-wrap"></span> input wrapper without getting any errors:

I have combined @Guicara's code

add_filter('wpcf7_form_elements', function($content) {
    $content = preg_replace('/<(span).*?class="\s*(?:.*\s)?wpcf7-form-control-wrap(?:\s[^"]+)?\s*"[^\>]*>(.*)<\/\1>/i', '\2', $content);

    return $content;
});

with @niaccurshi's answer and made some edits. First, you need to remove <span class="wpcf7-form-control-wrap"> with code above. Then manually add a <span class="wpcf7-form-control-wrap"> with extra class that matches your field name to your form template as a wrapper of all fields. Check example screenshot below:

enter image description here

Juraj
  • 458
  • 8
  • 18
0

In addition to Guicara's php code, the following javascript code could get the text of the error message:

document.addEventListener('wpcf7invalid',function(e){
    if ('validation_failed' === e.detail.apiResponse.status){
        $.each(e.detail.apiResponse.invalidFields,function(i,el){
        console.log('this is error response and form object',el.message, $('#'+el.idref));
        });
    }
});