Riesling
Riesling

Reputation: 6593

How to validate pattern matching in textarea?

When I use textarea.checkValidity() or textarea.validity.valid in javascript with an invalid value both of those always return true, what am I doing wrong?

<textarea name="test" pattern="[a-z]{1,30}(,[a-z]{1,30})*" id="test"></textarea>​

jQuery('#test').on('keyup', function() {
    jQuery(this).parent().append('<p>' + this.checkValidity() + ' ' +
    this.validity.patternMismatch + '</p>');
});

http://jsfiddle.net/Riesling/jbtRU/9/

Upvotes: 44

Views: 66128

Answers (5)

Lionel Rowe
Lionel Rowe

Reputation: 5926

All the other answers rely on jQuery/React — here's a vanilla JS one:

const invalid = { pattern: 'a', value: 'b' }
const textarea = Object.assign(document.createElement('textarea'), invalid)

if (textarea.checkValidity()) {
    const input = Object.assign(document.createElement('input'), invalid)

    document.body.addEventListener('input', (e) => {
        if (e.target.matches('textarea[pattern]')) {
            const pattern = new RegExp(`^(?:${e.target.getAttribute('pattern')})$`)

            e.target.setCustomValidity(
                pattern.test(e.target.value)
                    ? ''
                    : input.validationMessage
            )
        }
    })
}
textarea+.validation-msg::after { font-size: 0.8em; }
textarea:not(:invalid) { outline: 3px solid lawngreen; }
textarea:invalid { outline: 3px solid tomato; }
textarea:not(:invalid)+.validation-msg::after { content: "Valid!"; }
textarea:invalid+.validation-msg::after { content: "Invalid!"; }
<p>Enter 0-3 non-empty lines, with optional trailing newline:</p>
<textarea rows="5" pattern="(?:[^\n]+\n?){0,3}"></textarea>
<div class="validation-msg"></div>

The validation message is grabbed from an invalid dummy input element, so it will respect browser defaults, user agent language settings, etc.

Note that it'll handle the typical use case of invalid user input, but it won't handle situations such as programmatically changing the textarea's value, the textarea already being invalid when it's first rendered, etc. A more comprehensive solution would need to handle those scenarios with a MutationObserver or similar.

Upvotes: 0

Nyque
Nyque

Reputation: 391

In case there are others who use React-Bootstrap HTML form validation and not jQuery.

This does not explicitly use pattern but it works the same way.

I'm only making some changes from the documentation.

function FormExample() {
  const [validated, setValidated] = useState(false);
  const [textArea, setTextArea] = useState('');
  const textAreaRef = useRef(null);

  const handleSubmit = (event) => {
    const form = event.currentTarget;
    if (form.checkValidity() === false) {
      event.preventDefault();
      event.stopPropagation();
    }

    setValidated(true);
  };

  const isValid = () => {
    // Put whichever logic or regex check to determine if it's valid
    return true;
  };

  useEffect(() => {
    textAreaRef.current.setCustomValidity(isValid() ? '' : 'Invalid');
    // Shows the error message if it's invalid, remove this if you don't want to show
    textAreaRef.current.reportValidity();
  }, [textArea];

  return (
    <Form noValidate validated={validated} onSubmit={handleSubmit}>
      <Form.Row>
        <Form.Group md="4" controlId="validationCustom01">
          <Form.Label>Text area</Form.Label>
          <Form.Control
            required
            as="textarea"
            ref={textAreaRef}
            placeholder="Text area"
            value={textArea}
            onChange={(e) => setTextArea(e.target.value)}
          />
          <Form.Control.Feedback>Looks good!</Form.Control.Feedback>
        </Form.Group>
      </Form.Row>
      <Button type="submit">Submit form</Button>
    </Form>
  );
}

render(<FormExample />);

Upvotes: 0

Dipesh Shah
Dipesh Shah

Reputation: 581

HTML5 <textarea> element does not support the pattern attribute.

See the MDN doc for allowed <textarea> attributes.

You may need to define this functionality yourself.

Or follow the traditional HTML 4 practice of defining a JavaScript/jQuery function to do this.

Upvotes: 46

dwkd
dwkd

Reputation: 2896

This will enable the pattern attribute on all textareas in the DOM and trigger the Html5 validation. It also takes into account patterns that have the ^ or $ operators and does a global match using the g Regex flag:

$( document ).ready( function() {
    var errorMessage = "Please match the requested format.";

    $( this ).find( "textarea" ).on( "input change propertychange", function() {

        var pattern = $( this ).attr( "pattern" );

        if(typeof pattern !== typeof undefined && pattern !== false)
        {
            var patternRegex = new RegExp( "^" + pattern.replace(/^\^|\$$/g, '') + "$", "g" );

            hasError = !$( this ).val().match( patternRegex );

            if ( typeof this.setCustomValidity === "function") 
            {
                this.setCustomValidity( hasError ? errorMessage : "" );
            } 
            else 
            {
                $( this ).toggleClass( "error", !!hasError );
                $( this ).toggleClass( "ok", !hasError );

                if ( hasError ) 
                {
                    $( this ).attr( "title", errorMessage );
                } 
                else
                {
                    $( this ).removeAttr( "title" );
                }
            }
        }

    });
});

Upvotes: 9

Yann D&#236;nendal
Yann D&#236;nendal

Reputation: 1627

You can implement this yourself with setCustomValidity(). This way, this.checkValidity() will reply whatever rule you want to apply to your element. I don't think this.validity.patternMismatch can set manually, but you could use your own property instead, if needed.

http://jsfiddle.net/yanndinendal/jbtRU/22/

$('#test').keyup(validateTextarea);

function validateTextarea() {
    var errorMsg = "Please match the format requested.";
    var textarea = this;
    var pattern = new RegExp('^' + $(textarea).attr('pattern') + '$');
    // check each line of text
    $.each($(this).val().split("\n"), function () {
        // check if the line matches the pattern
        var hasError = !this.match(pattern);
        if (typeof textarea.setCustomValidity === 'function') {
            textarea.setCustomValidity(hasError ? errorMsg : '');
        } else {
            // Not supported by the browser, fallback to manual error display...
            $(textarea).toggleClass('error', !!hasError);
            $(textarea).toggleClass('ok', !hasError);
            if (hasError) {
                $(textarea).attr('title', errorMsg);
            } else {
                $(textarea).removeAttr('title');
            }
        }
        return !hasError;
    });
}

Upvotes: 14

Related Questions