Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

Use the CSS selector tag:not([attribute]) to match elements that do not have a particular attribute. For example, img:not([alt]) finds images with no alt attribute. To retrieve every match in JavaScript, pass the selector to document.querySelectorAll().

Use :not() with an attribute selector

The general pattern is:

tag:not([attribute])

Replace tag with the element name and attribute with the attribute to check. The bracketed part, [attribute], matches elements that have that attribute. Wrapping it in :not(...) reverses the test, so the complete selector matches elements of the chosen type that lack it.

For example, img:not([alt]) selects <img> elements without an alt attribute. The selector is CSS, so you can use it in a stylesheet or pass it to a DOM query method in JavaScript.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Common selectors

  • img:not([alt]) — images without an alt attribute.
  • a:not([href]) — links without an href attribute.
  • input:not([name]) — inputs without a name attribute.

These selectors check for a particular attribute on a particular element type. If you want to check a different element or attribute, substitute those names; the structure stays the same.

Find all matching elements with JavaScript

Use document.querySelectorAll() when you need every match in the document. This runnable example logs each image that lacks alt:

const missingAlt = document.querySelectorAll('img:not([alt])');

missingAlt.forEach((image) => {
  console.log(image);
});

querySelectorAll() returns a static NodeList in document order. “Static” means the result does not update automatically if the page changes after the query. If your code adds or changes elements later, run the query again to get a new result.

If nothing matches, the result is an empty NodeList. That is not an error: it means there were no elements matching the selector at the time the query ran. To inspect only the number of matches, read missingAlt.length; to inspect the elements, iterate over the list as in the example.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

First match versus every match

Use querySelector() if you need only the first matching element. It returns that element, or null if there is no match. Use querySelectorAll() for an audit or other task that must examine every matching element.

Limit the search to a container

To check a section instead of the whole document, first get the container, then query beneath it. Prefixing the selector with :scope makes the query root explicit:

Rank #2
Sale
HTML and CSS: Design and Build Websites
  • HTML CSS Design and Build Web Sites
  • Comes with secure packaging
  • It can be a gift option
const container = document.querySelector('#content');

if (container) {
  const missingAlt = container.querySelectorAll(':scope img:not([alt])');
  missingAlt.forEach((image) => {
    console.log(image);
  });
}

The if check matters because document.querySelector('#content') can return null if the page has no element with that ID. Calling a method on null would fail, so verify that the container exists before querying it.

An Element’s querySelectorAll() searches its descendants. The :scope prefix clarifies that the selector is evaluated relative to the chosen query root. This is useful when an audit should be confined to one region, such as a content panel, rather than collecting matching elements from unrelated page areas.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Distinguish a missing attribute from an empty one

img:not([alt]) matches an image when the alt attribute is absent. It does not match <img alt="">, because that markup has the attribute even though its value is empty.

To select images whose alt value is explicitly empty, use:

img[alt=""]

These are different markup states, so choose the selector according to the question you are trying to answer:

  • Use img:not([alt]) to find images with no alt attribute.
  • Use img[alt=""] to find images with an explicitly empty alt value.

Finding either state does not, by itself, establish what the attribute’s value should be. The appropriate markup depends on the image and its context; an audit should identify the state without assuming that every match requires the same fix.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Check other attributes and values

The same presence test works for any named attribute. For example, a:not([href]) looks for anchors without href, and input:not([name]) looks for inputs without name. The selector checks absence, not whether an attribute contains a useful or valid value.

If the attribute exists and you need to test its value, use an attribute selector that states the value you care about. For instance, [alt=""] tests for an empty value. Presence and value checks answer different questions; do not use :not([attribute]) to find an attribute that is present but blank.

Attribute names in HTML are case-insensitive. Attribute-value matching follows the rules for the attribute and language involved, so do not assume every value comparison is case-insensitive.

Use the selector for inspection or styling

A CSS selector can be useful in two different ways. In JavaScript, it identifies elements for inspection or follow-up code. In a stylesheet, it can apply a style to matches. For example, a temporary outline can help visually locate images missing alt:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
img:not([alt]) {
  outline: 3px solid red;
}

This only changes how matching elements are displayed; it does not add an attribute or repair the markup. For a programmatic audit, query the elements and decide what information to report or what correction is appropriate in your own workflow.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Troubleshoot selector results

The query throws a syntax error

An invalid selector passed to querySelectorAll() throws a SyntaxError DOMException. Check that the brackets and parentheses are balanced, the selector is valid CSS, and attribute names are written inside square brackets. A correct example is img:not([alt]).

The result is empty

An empty result means the query found no matches at the time it ran. Check that the element type and attribute name are the ones present in the markup, and confirm that the elements have been added to the document before querying. Also check whether you intended to include empty values: img:not([alt]) does not include img elements with alt="".

The query finds elements outside the intended area

A query made on document searches the document. If you want a narrower result, query on the container element and use :scope to make the root explicit, as shown above. Confirm that the container selector identifies the intended element before running the descendant query.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A dynamically built selector fails

If you interpolate an ID or class value into a selector, the value may not be a valid CSS identifier as written. Escape it with CSS.escape() before building the selector. This caveat concerns values inserted into selector syntax; the fixed attribute-name examples above do not require dynamic escaping.

The result becomes outdated

A querySelectorAll() result is static, not a live collection. If the DOM changes after the query, rerun the query when you need current matches. Do not assume the original NodeList will gain or lose elements as the page changes.

Or skip the browser setup

For a visual screenshot of a page, ScreenshotNeo provides a one-call screenshot API. It does not replace a DOM query when the task is to identify elements missing attributes; use the selector examples above for that inspection. A screenshot is useful when you also need a visual capture of the page.

cURL:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

See the ScreenshotNeo API documentation for options. The same request is available in Python:

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)

And in Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
  • It accepts cookie or consent banners as a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off.
  • Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing. Response headers identify the page verdict and whether the request was billed.
  • An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.
  • The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots; every feature is available on every plan.

Sign up for 1,000 free screenshots a month with no card.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.