Direct DOM manipulation or referencing is typically discouraged by newer JavaScript frameworks. However it may still be helpful or even necessary to use plain JS at times, perhaps when working with more complicated controls, integrating with third-party code, or handling cases not covered by particular frameworks.

Here is an example of using vanilla JavaScript to return invalid fields in an HTML5 form:

<form action="/users/new" method="post">
  <label for="first_name">First name:</label>
  <input type="text" id="first_name" name="first_name" required="required"><br><br>
  <label for="last_name">Last name:</label>
  <input type="text" id="last_name" name="last_name" required="required"><br><br>
  <input type="submit" value="Submit">
</form>

In this form the two name fields are required. If it were necessary to detect invalid fields, the following could be used:

const form = document.getElementsByTagName('form')[0] // or getElementById(), etc.
const invalidFieldNames = Array.from(form.querySelectorAll(':invalid')).map(field => field.name)

:invalid is a pseudo-class provided by HTML5 validation.

Now invalidFieldNames contains a list of the invalid fields, and could be integrated with other errors or functionality.

This was a simple example, but a more realistic one might involve hidden inputs and complicated controls, or some other scenario that isn’t covered by standard form validation.

Here is a function which detects the presence of errors for a form:

function hasErrors(f) {
  f.querySelectorAll(':invalid').length > 0
}

Or, using the first bit of code to create a function returning all form errors:

const formErrors = (f) => Array.from(f.querySelectorAll(':invalid')).map(field => field.name)