Moving to plain JavaScript
Everything jQuery does, the browser can now do by itself. Dropping the library makes the page about 30 KB smaller (compressed) and removes a dependency. You do not have to rewrite the whole project: code is moved over gradually, one file at a time.
Equivalents table
| jQuery | Plain JavaScript |
|---|---|
$(function () {}) | document.addEventListener('DOMContentLoaded', fn) |
$('#tasks') | document.querySelector('#tasks') |
$('li') | document.querySelectorAll('li') |
$el.find('li') | el.querySelectorAll('li') |
$el.closest('li') | el.closest('li') |
$el.parent() | el.parentElement |
$el.text() | el.textContent |
$el.html() | el.innerHTML |
$el.attr('href') | el.getAttribute('href') |
$el.prop('checked') | el.checked |
$el.data('id') | el.dataset.id (a string) |
$el.addClass('done') | el.classList.add('done') |
$el.toggleClass('done') | el.classList.toggle('done') |
$el.css('color', 'red') | el.style.color = 'red' |
$('<li>') | document.createElement('li') |
$list.append($item) | list.append(item) |
$el.remove() | el.remove() |
$el.on('click', fn) | el.addEventListener('click', fn) |
$list.on('click', 'li', fn) | a handler on list checking event.target.closest('li') |
$input.val() | input.value |
$form.serialize() | new URLSearchParams(new FormData(form)).toString() |
$('li').each(fn) | document.querySelectorAll('li').forEach(fn) |
$.getJSON(url) | fetch(url).then((r) => r.json()) |
$el.fadeOut() | a CSS transition or el.animate() |
$.extend({}, a, b) | { ...a, ...b } |
$.Deferred() | new Promise() |
The to-do list without jQuery
The same to-do list as in the delegation lesson, without the library:
<style>
.done { text-decoration: line-through; color: gray; }
</style>
<input id="title" placeholder="New task">
<button id="add">Add</button>
<ul id="tasks"></ul>
<script>
const list = document.querySelector('#tasks');
const input = document.querySelector('#title');
document.querySelector('#add').addEventListener('click', () => {
const item = document.createElement('li');
const title = document.createElement('span');
title.className = 'title';
title.textContent = input.value;
const button = document.createElement('button');
button.className = 'delete';
button.textContent = 'Delete';
item.append(title, ' ', button);
list.append(item);
});
list.addEventListener('click', (event) => {
if (event.target.closest('.delete')) {
event.target.closest('li').remove();
} else if (event.target.closest('.title')) {
event.target.closest('.title').classList.toggle('done');
}
});
</script>It behaves the same: tasks are added, marked with a click and removed with the button. There is a little more code, but it does not depend on a library.
How to migrate
- Write new code without jQuery.
- Convert old files one by one, checking the page after each.
- Search the project for remaining
$(andjQuery(calls. When none are left, remove the jQuery<script>tag. - Replace jQuery plugins (sliders, masks) with modern dependency-free libraries; while they remain, jQuery cannot be removed.
If you need to upgrade jQuery to a newer version rather than remove it, the official jQuery Migrate plugin helps: it prints warnings about deprecated calls to the console.
Common mistake
Carrying over the habit of acting on the whole set: document.querySelectorAll('li').classList.add('done'). The list returned by querySelectorAll() has no classList, so this throws. Without jQuery each element is handled in a loop: document.querySelectorAll('li').forEach((li) => li.classList.add('done')). Also remember that querySelector() returns null when nothing is found, whereas an empty jQuery set silently did nothing.