Selectors & DOM Manipulation
CSS-style $() selectors, reading and writing content and attributes, and traversing the DOM.
$() selectors
jQuery's selector engine accepts the same syntax as CSS, plus a handful of its own extensions:
$('#header'); // by ID
$('.card'); // by class
$('p'); // by tag name
$('ul li'); // descendant selector
$('input[type="email"]'); // by attribute
$('li:first'); // jQuery extension — first matching element
$('li:eq(2)'); // jQuery extension — element at index 2
Every call to $() returns a jQuery object — an array-like wrapper around zero or more matched DOM elements — which is what gives you access to all of jQuery's methods (.text(), .on(), .css(), etc.) on the result.
const $items = $('.list-item');
console.log($items.length); // number of matched elements
console.log($items[0]); // the raw underlying DOM element, unwrapped
Reading and writing content
$('#title').text(); // get the text content
$('#title').text('New Title'); // set the text content
$('#content').html(); // get the inner HTML
$('#content').html('<strong>Bold</strong>'); // set the inner HTML
$('#email').val(); // get a form field's value
$('#email').val('a@example.com'); // set a form field's value
.text() always treats its argument as plain text (escaping any HTML), while .html() parses its argument as markup — using .text() for user-supplied content and reserving .html() for content you trust is the standard way to avoid accidentally introducing an XSS vulnerability.
Manipulating classes and attributes
$('#card').addClass('highlighted');
$('#card').removeClass('highlighted');
$('#card').toggleClass('active'); // adds it if absent, removes it if present
$('#card').hasClass('active'); // true/false
$('#link').attr('href', '/new-path'); // set an attribute
$('#link').attr('href'); // read an attribute
$('#checkbox').prop('checked', true); // set a boolean DOM property
.attr() and .prop() overlap but aren't identical — .prop() is the correct choice for boolean state (checked, disabled, selected), since those are live DOM properties rather than plain HTML attribute strings; .attr() is right for everything else.
Styling directly
$('#box').css('background-color', '#3b82f6');
$('#box').css({
color: 'white',
padding: '1rem',
borderRadius: '8px',
});
Setting styles directly with .css() is convenient for one-off dynamic values, but for anything reusable, toggling a CSS class with .addClass()/.toggleClass() keeps styling in your stylesheet rather than scattered across JavaScript.
DOM traversal
jQuery makes moving around the DOM tree relative to an element straightforward:
$('.card').parent(); // the immediate parent element
$('.card').parents('.grid'); // every matching ancestor, however deep
$('.card').closest('.grid'); // the nearest matching ancestor, stopping at the first match
$('.card').children(); // direct children only
$('.card').find('.title'); // any descendant matching the selector, at any depth
$('.card').siblings(); // every sibling element
$('.card').next(); // the immediately following sibling
$('.card').prev(); // the immediately preceding sibling
.find() searches descendants (any depth), while .children() only looks at direct children — a common source of confusion when a nested structure doesn't match at the level expected.
<div class="grid">
<div class="card">
<h3 class="title">Card One</h3>
<div class="body">
<span class="title">Nested span, not a heading</span>
</div>
</div>
</div>
$('.card').find('.title'); // matches BOTH .title elements — any depth
$('.card').children('.title'); // matches NEITHER — .title isn't a direct child of .card
Creating and inserting elements
const $newItem = $('<li>').addClass('list-item').text('New item');
$('#list').append($newItem); // insert as the last child
$('#list').prepend($newItem); // insert as the first child
$('#list').before($newItem); // insert before #list, as a sibling
$('#list').after($newItem); // insert after #list, as a sibling
$('.list-item').remove(); // remove matched elements entirely from the DOM
$('.list-item').empty(); // remove children, but keep the element itself
Chaining
Because most jQuery methods return the same jQuery object they were called on, calls can be chained — a hallmark of jQuery's fluent API style:
$('#card')
.addClass('highlighted')
.css('opacity', 1)
.find('.title')
.text('Updated title');
Each call in the chain operates on the result of the previous one — .find('.title') narrows the selection from #card down to its .title descendant, and the final .text(...) applies to that narrowed selection.
Common mistakes
- Confusing
.find()(descendants at any depth) with.children()(direct children only) and getting an empty or unexpectedly large result set. - Using
.html()to insert untrusted, user-supplied content — this is a direct cross-site scripting (XSS) risk; use.text()for anything not already sanitized. - Re-querying the DOM repeatedly inside a loop (
$('.item')called on every iteration) instead of caching the jQuery object once in a variable — this needlessly re-runs the selector engine every time. - Using
.attr('checked', true)to check a checkbox instead of.prop('checked', true)— the attribute-based approach doesn't reliably reflect the live DOM state.