Hardware FixRecommendedDevice not working? Your driver may be the problemCheck updates for common hardware issues.Fix DriversOctober DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsWindows FixRecommendedWindows errors stealing your time? Find the fix fastScan stability, cleanup and performance issues.Fix Now×
Skip to content
Cheerio

How to Find HTML Elements by Multiple Tags with Cheerio

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

Use one comma-separated CSS selector with Cheerio: const headings = $('h1, h2');. The comma means “match an h1 or an h2.” First load your markup with cheerio.load(), then call the returned $ function with the selector list.

The direct method: a comma-separated selector

Cheerio uses CSS selector syntax. To select several HTML tag names in one query, place the tags in a comma-separated list:

const cheerio = require('cheerio');

const $ = cheerio.load('<h1>Title</h1><p>Body</p><h2>Section</h2>');
const headings = $('h1, h2');

The load() call creates a document-bound query function named $. The selector h1, h2 contains two alternatives, so the result includes every h1 and every h2 in the loaded document.

What the comma means—and what it does not mean

Comma-separated tags are alternatives

In $('h1, h2, h3'), the comma separates independent selector branches. An element only needs to match one branch to be selected. Add or remove tag names as your extraction task changes:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const headings = $('h1, h2, h3');
const textBlocks = $('p, blockquote, li');
const media = $('img, video, audio');

Adjacent selector parts are combined conditions

Do not confuse a comma with adjacent selector syntax. p.selected means a p element that also has the selected class. It is not equivalent to p, .selected, which means any paragraph or any element with that class.

const selectedParagraphs = $('p.selected'); // both conditions
const paragraphsOrSelected = $('p, .selected'); // either branch

Use commas when you want different tag names. Use a compound selector when every match must satisfy multiple conditions.

A complete example you can adapt

This example loads a small document, selects three tag types, and prints each element’s tag name and text:

const cheerio = require('cheerio');

const html = `
  <article>
    <h1>Page title</h1>
    <p>Introduction</p>
    <h2>Details</h2>
    <div>Other content</div>
  </article>
`;

const $ = cheerio.load(html);
const wanted = $('h1, h2, p');

wanted.each((_, element) => {
  console.log(element.tagName, $(element).text());
});

The selector is the only part that changes when you need a different set of tags. For example, replace it with $('title, meta, link') to inspect those element types, or with $('table, ul, ol') to collect list and table containers.

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

Limit the search to a specific part of the document

A global query searches the loaded document. When a page contains repeated components, scope the query to a container so that unrelated matches are excluded.

Pass a context

Cheerio’s query function accepts a selector and a context. Select the container first, then use that selection as the context for the tag list:

const article = $('.article');
const content = $('h2, p', article);

Here, only h2 and p descendants of .article are considered.

Use .find() for descendant searches

The equivalent descendant query uses .find():

const content = $('.article').find('h2, p');

.find('h2, p') searches for either tag inside the current selection. This is useful when you keep a reference to a component and perform several operations on its descendants.

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.

Scope nested components separately

If a document contains multiple articles, iterate over each one and run the same comma selector within that article:

$('.article').each((index, article) => {
  const headingAndText = $(article).find('h2, p');
  console.log('Article', index, headingAndText.text());
});

Scoping this way prevents content from one component being mixed with another in your output.

Combine multiple tags with classes and attributes

Each branch in a comma-separated selector can have its own additional conditions. This lets you express “these tags, but only when they meet their respective rules”:

const targets = $('h1.page-title, h2.section-title, p.lede');

The first branch selects an h1 with page-title; the second selects an h2 with section-title; the third selects a p with lede. A branch can also use an attribute selector:

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const resources = $('a[rel="nofollow"], img[loading="lazy"]');

Keep each branch readable. If a selector becomes difficult to review, select a stable candidate set first and apply additional checks in JavaScript.

Handle untrusted values safely

Do not interpolate attacker-controlled text directly into selector syntax. A value inserted into an attribute selector can change the selector itself. Instead, select candidates with a fixed selector and compare the attribute value as data with .filter().

const wantedId = userProvidedId;

const matches = $('*[data-id]').filter((_, element) => {
  return $(element).attr('data-id') === wantedId;
});

The selector *[data-id] is constant. The untrusted value is used only in a JavaScript equality comparison, so quote characters or selector operators in the input cannot alter the query.

Extract values from the matched set

Read text

Calling .text() on a selection returns its combined text. For per-element processing, iterate with .each():

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const labels = [];

$('h1, h2, h3').each((_, element) => {
  labels.push($(element).text().trim());
});

console.log(labels);

Read attributes by tag

Different tag branches often expose different attributes. Check the tag name before reading the relevant value:

const records = [];

$('a, img').each((_, element) => {
  const node = $(element);
  const tag = element.tagName;

  records.push({
    tag,
    value: tag === 'a' ? node.attr('href') : node.attr('src'),
    text: node.text().trim()
  });
});

This keeps the multi-tag selection in one query while preserving tag-specific extraction logic.

Keep the original element available

The callback receives the underlying element. Wrap it with $(element) whenever you need Cheerio methods such as .text(), .attr(), or .find().

Common mistakes and fixes

Only the last tag appears

Cause: the code called separate queries and overwrote the first variable, or used a selector without a comma.

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

Fix: keep the alternatives in one string, such as $('h1, h2, h3'), or store separate selections under different names.

The result is empty

Cause: the markup loaded by Cheerio does not contain the requested tags, the selector has a spelling error, or the query is scoped to the wrong context.

Fix: print or inspect the input HTML, test a simple selector such as $('body').length, then add the tag branches and context one at a time.

Unrelated content is included

Cause: a document-wide query is selecting matching tags outside the component you need.

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

Fix: select the container first and call .find('tag1, tag2'), or pass the container as the query context.

A compound selector returns no matches

Cause: a selector such as p.selected requires both the tag and class on the same element.

Fix: use a comma if you meant alternatives, for example p, .selected, or correct the class and attribute conditions if both are required.

Input changes the selector’s meaning

Cause: user-controlled text was concatenated into a selector.

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

Fix: use a fixed candidate selector and compare the untrusted value in .filter(), as shown above.

Performance and maintainability considerations

A single comma-separated query is usually easier to maintain than several nearly identical loops. Keep the selector close to the extraction code, give the resulting selection a descriptive name, and scope it to the smallest useful container. Narrow contexts reduce accidental matches and make later markup changes easier to diagnose.

If each tag requires substantially different processing, one combined query can still be correct, but separate clearly named branches may be easier to read. Choose based on whether shared iteration logic outweighs tag-specific handling.

Version and scope note

This guidance reflects the Cheerio documentation available on September 29, 2026. Selector support can change between releases, so check the documentation for the exact Cheerio version pinned by your project. The documented API uses CSS selectors, load() to create the query function, comma-separated alternatives, contexts, and .find() for descendant searches.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

If your next step is capturing the rendered page rather than parsing supplied HTML, ScreenshotNeo provides a single website-screenshot request. It accepts a URL and returns a PNG, JPEG, WebP, or PDF; you do not need to configure a browser locally.

Cookie and consent banners, newsletter popups, and chat widgets are removed before the shot. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients.

cURL

See the ScreenshotNeo API documentation for the complete option list.

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

Python

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)

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}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const body = Buffer.from(await res.arrayBuffer());
require('fs').writeFileSync('shot.webp', body);

ScreenshotNeo includes full-page capture with lazy images loaded, element capture by CSS selector, custom CSS and JavaScript, waits for a selector, delay, or network idle, device presets and custom viewports, dark mode, retina scale, PDF controls, request and resource blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, selectable cache TTLs, signed links, asynchronous jobs with signed webhooks, bulk capture for up to 100 URLs per call, a usage API, and an OpenAPI specification. Parameter names used by other screenshot APIs also work for easier migration.

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

The Free plan includes 1,000 shots per month with no card. Paid plans start at $5 for 3,000 shots; higher plans are Growth at $15 for 15,000, Pro at $39 for 60,000, Scale at $99 for 250,000, and Business at $249 for 1,000,000. Yearly billing provides two months free, and every feature is available on every plan. Sign up free for ScreenshotNeo to get the 1,000 monthly shots without a card.

Frequently Asked Questions

Does the selector need spaces around the comma?

No. Both h1,h2 and h1, h2 express the same two alternatives; the spaced form is generally easier for people to read.

Can I add another tag later without changing the extraction loop?

Yes. Extend the selector string, for example from $('h1, h2') to $('h1, h2, h3'), while keeping the same iteration code if the processing is shared.

Which Cheerio version should I use?

Pin the version your project supports and consult that version’s documentation, because selector-engine behavior and APIs may change across releases.

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

The Bottom Line

For multiple tag names in Cheerio, use one comma-separated CSS selector—such as $('h1, h2, p')—and add a context or .find() when the search must stay inside a component.

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.

Leave a Reply

Your email address will not be published. Required fields are marked *

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

Read next

Recommended PC Tool
Recommended PC Tool
Crashes, No Sound, or Screen Glitches?Free driver scan
Windows Errors? Fix Them Before They SpreadFree repair scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.