What is the best way to customize HTML5 input validation styling without completely removing the default browser styling?

As I work on creating an address form for my web application, I find myself struggling to define the styling rules for HTML5 form validation that includes the required attribute. The examples and behaviors discussed below are all specific to Firefox.

The HTML code for the initial input field in the form is as follows:

<label for="addressLine1" class="form__label">
        Address Line 1
</label>
<input type="text" required aria-required="true" class="form__input-text" id="addressLine1"/>

Without any custom styling, the behavior of the input is described as follows:

  1. Upon page loading, the input field presents default styling for a text input

https://i.sstatic.net/Li29r.png

  1. If attempting to submit the form with a required input left blank, the browser will highlight the input with a red border or shadow

https://i.sstatic.net/JboMY.png

I aim to preserve this behavior where the input initially displays default styling upon load and only switches to "invalid" styling if a user attempts to submit the form with any required fields incomplete. However, determining which attributes/pseudo-classes need adjusting to modify the styling while retaining this behavior has proven challenging. Using the :invalid pseudo-class results in the following behavior:

  1. At load, the input already showcases my "invalid" styling due to the field being empty

https://i.sstatic.net/aUcJz.png

  1. Submitting the form with a blank field adds the red border/shadow atop my existing "invalid" styling

https://i.sstatic.net/udcZE.png

  1. Only by entering valid data into the input can I achieve my desired default/valid styling

https://i.sstatic.net/fJSZK.png

How can one maintain the default behavior (default styling onload, invalid styling on incorrect submission) with personalized styles? Is it achievable solely through CSS, or must additional JavaScript functionality be implemented?

Answer №1

Well, after a thorough review of the CSS Pseudo Class documentation on MDN, it seems that there isn't a combination of pseudo classes that can accurately represent the different states necessary for this behavior to function correctly. I experimented with various options and also looked into the Bootstrap validation link provided by Alex Schaeffer, but decided against adding unnecessary dependencies or stylesheets. Instead, I devised a solution that requires minimal additional CSS and JavaScript.

To begin with, I discovered that the red border effect was actually a box shadow which could be overridden using the following (S)CSS:

.form__input-text {
    /* default input styling goes here */
    box-shadow: none;
}

Next, I introduced a simple state mechanism within my component to track whether the form has been validated. Since I am using Svelte, I achieved this by creating a boolean variable inside the component's <script> tag as follows:

let wasValidated = false;

I then included a conditional class in my HTML/JSX code. In other frameworks or when using jQuery/vanilla JS, you may need to handle this explicitly with a function connected to an event handler. In Svelte, all I had to do was modify my markup like this:

<label for="addressLine1" class="form__label">
        Address Line 1
</label>
<input 
    type="text" 
    required aria-required="true" 
    class="form__input-text"
    class:wasValidated="{wasValidated}"
    id="addressLine1"
/>

The

class:wasValidated="{wasValidated}"
part adds a .wasValidated class to the input element conditionally based on the truthiness of the wasValidated variable.

In my (S)CSS, I then added styling to apply the "invalid" look (which currently changes the border color to red) only to elements that have been validated at least once and are invalid:

input.wasValidated:invalid {
    border-color: $red;        
}

Subsequently, I linked a straightforward onClick function to the submit button to set the wasValidated variable to true upon clicking:

HTML/JSX

<button on:click|preventDefault={onClick} class="form__submit-button" type="submit">
    Search
</button>

JS

const onClick = e => {
    wasValidated = true;
};

This function must be associated with a click event rather than a submit event because the submit event does not trigger if the form fails validation.

Now, when the page initially loads, all form inputs exhibit the default style regardless of their validity since wasValidated is set to false. Upon clicking the submit button, wasValidated switches to true, applying the .wasValidated class to any required elements. If these elements are invalid, they display the "invalid" style. Alternatively, successful form submission is handled by the onSubmit function attached to the form.

Edit: In Svelte, you can detach event handlers after their initial triggers. Consequently, my submit button markup now includes the following:

<button on:click|preventDefault|once={onClick} class="form__submit-button" type="submit">
    Search
</button>

Adding the |once modifier to on:click detaches the onClick function after the first click, preventing unnecessary firing if the user attempts to submit invalid data multiple times.

Answer №2

Is it possible to maintain the default behavior (default styling on load, invalid styling on invalid submission) while applying custom styles? Can this be achieved using only CSS or do I need to incorporate some JavaScript?

You can accomplish this with just a few lines of JavaScript.

If your input appears as invalid, it is likely because it is both empty and required.

Here is a simple 3-step approach:

Step 1: Include the required attribute in your HTML element

<input type="text" required>

Step 2: Then, immediately remove that attribute using JavaScript

const addressLine1Input = document.getElementById('addressLine1');
addressLine1Input.removeAttribute('required');

Step 3: Once a character is entered into the <input>, use JavaScript again to reapply the required attribute

const setRequired = (e) => e.target.required = 'required';
addressLine1Input.addEventListener('keyup', setRequired, false);

To test this functionality, add and then delete characters from the <input>. You will observe that when empty, the field does not show as invalid, but once populated and emptied again, it will display as invalid.

See the working example below:

const addressLine1Input = document.getElementById('addressLine1');
addressLine1Input.removeAttribute('required');

const setRequired = (e) => e.target.required = 'required';
addressLine1Input.addEventListener('keyup', setRequired, false);
input:invalid {
  background-color: rgba(255, 0, 0, 0.3);
  border: 2px solid rgba(255, 0, 0, 0.5);
}
<form>
<label for="addressLine1" class="form__label">Address Line 1</label>
<input type="text" name="addressLine1" id="addressLine1" class="form__input-text" placeholder="Enter address here..." aria-required="true" required>
</form>

Answer №3

It seems that achieving this effect solely with CSS is not possible; JavaScript is also required

CSS

#addressLine1{
    border: none;
    background: none;
    outline: none;
    border-bottom: 1px solid black;
}

JS

document.getElementById('form_id').addEventListener('submit',function(e){
    let address = document.getElementById('addressLine1').value
    if(address == ""){
        e.preventDefault()
        address.style.borderBottomColor = "red";
    }else{
        address.style.borderBottomColor = "black";
    }
})

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What could be causing the issue of CSS animation not functioning properly when used in conjunction with a

I am working on creating a switch button with CSS and JavaScript that needs an animation when toggling or clicked. The only issue I am facing is related to the animation. I am wondering if there might be any problem with the positioning (relative, absol ...

Looping through an array in PHP

Is there a way to loop through an array in my code? Here is the initial version of my script: $val=array( array("value" => "Male","label" => "Male"), array("value" => "Female","label" => "Femal"), ); my_form("Gender","select","gender",$va ...

Move the aircraft across the display in a lively animation

I need help creating an animation where a plane flies across the screen, exits from the left side, and re-enters from the right side in a continuous loop. How can I achieve this effect to make the plane fly endlessly across the screen? Here is my code: ...

Trigger submission issue encountered with Jquery in a dialog (modal) window

It's somewhat difficult for me to fully explain my problem. Nevertheless, I'll give it a go. In my HTML code, I have implemented several triggers using Jquery. Trigger 1 is responsible for validating specific items within a form. Once Trigger 1 ...

I need to send information from my JavaScript code to my Flask server

I'm having an issue with transferring data from JavaScript code in an HTML template to my Flask server. Specifically, I want to send geolocation coordinates (latitude and longitude) obtained through JavaScript to my Flask server, but I'm unsure o ...

Replacing a div with another div can be achieved in a single swap

My goal is to swap one div with another div upon clicking using the provided code. It functions properly for a single instance. However, the issue arises when the class appears multiple times, causing all instances to change due to sharing the same class n ...

I am attempting to properly arrange my navigation links and logo in alignment

I need help aligning the logo in the exact center while keeping the navigation links as they are. Here is how it currently looks: https://i.stack.imgur.com/qOgVJ.jpg My CSS: .logo { display: inline-block; text-align: center; } na ...

Using Selenium WebDriver to retrieve the tagname of an element

I am trying to use python to extract the name of the element data-test-carrier-name, specifically looking for "trenitalia" as the result. The relevant HTML snippet is: div class="_1moixrt _dtnn7w" tabindex="0"span data-test-carrier-name="trenitalia" My ...

In terms of performance, is it better to fetch JSON data and render it using JavaScript, or to request the full

Similar Inquiry: Why is it a bad practice to return generated HTML instead of JSON? Or is it? When making an AJAX request to a PHP file, which method would result in quicker HTML rendering? Directly sending fully formatted HTML from PHP, Or sending ...

Gradually appear/disappear div element with a delay added

Storing divs in an array and deleting them after appending is my current method. Now, I'm looking to incorporate a fade-in and fade-out effect on each div with a delay. Check out this code snippet : var arr = $(".notification"); function display ...

Issue with loading the main.css file

Getting Started Managing two domains can be a challenge, especially when trying to make them appear as one seamless website. In this case, I have ownership of and . Goal My goal is to merge the content of https://mauricevandorst.com/personal-page/index ...

Are you curious about how jQuery can be used to group buttons together?

In my ASP.NET MVC application, I incorporate JQUERY to enhance the user experience. I am curious if there is a way to group buttons together, not radio buttons but regular buttons. Can I have buttonA + buttonB + buttonC grouped together with some space i ...

Click here to open in a new tab and experience the ever-changing dynamic

Imagine a scenario where a property site displays the main viewed property ad at the top, with smaller ads in square divs below. When a user clicks on one of the smaller ads, it dynamically swaps places with the main ad thanks to JavaScript. All ad data is ...

Expand your dropdown options with a Popup Input feature in the ComboBox to seamlessly add a 'New Option'

Hello there! I am currently learning, so please be patient with me. Currently, I am in the process of developing a web application for a product management system. The company I work for purchases products from multiple vendors, both through wholesale and ...

Is there a way to incorporate a <u> tag into an inline angular variable in HTML that has been generated by a ternary expression?

Currently, I have the following scenario. <label> Make {{$ctrl.tcTemplate.isLibrary ? $ctrl.tcTemplate.name : "this item"}} a Library item: <toggle ng-change="changed(); $ctrl.eraseName();" ng-model="$ctrl.tcTemplate.isLibrary;" off="Fals ...

`javascript pop up notification will only display one time`

I am currently developing a chrome extension with two different modes. When the user clicks on the icon, a pop-up message should appear to indicate which mode is active. However, I am facing an issue where the message does not always display when the pop- ...

Changing the event when a class is active in Vue3

Question I am looking for a way to trigger an event when the 'active' class is added to an element. How can I achieve this? I believe this could potentially be accomplished using a watcher method, but I am unsure how to watch for the applicatio ...

Validator Express, a versatile tool that inspects various types of forms

Using Express Validator has presented a challenge for me. At the moment, I have a single route with a variable (action) to send updated user data to the server. My approach involves a switch statement to handle different actions and update user data acco ...

Attempting to create a dynamic layout for the objects using media queries and flexbox, however, the results are not as expected

.container { display: flex; justify-content: center; align-items: center; flex-wrap: wrap; flex-direction: column; } .item { margin: 10px; border: 1px solid lightgray; border-radius: 10px; overflow: hidden; width: 100%; } @media scree ...

Cannot close the Bootstrap dropdown after selecting an option

I am encountering an issue with a dropdown list that has a select feature using Bootstrap 3.4.1. The problem is that the dropdown remains open after selection and does not close unless I click outside of the list. Here is the HTML code: <div id="c ...