jQuery UI & DataTables-Style Widgets
A widget is a ready-made interface component: a date picker, a dialog box, an autocomplete box, a sortable list, a data grid with search and paging. Admin panels, ERP screens and school management systems built in the jQuery years are full of them, usually from jQuery UI (the official widget collection) and DataTables (the most common table plugin). As a maintainer you rarely write a widget, but you constantly configure, update and debug them.
This lesson is a practical overview: what these libraries are, where they stand in 2026, the life cycle every widget follows, and which native HTML features can replace them in new code. The examples are our own; the official documentation (api.jqueryui.com and datatables.net) lists every option.
Where the main libraries stand
| Library | Latest version (checked November 2026) | Status |
|---|---|---|
| jQuery UI | 1.14.2 (January 2026) | Maintenance only: kept compatible with new jQuery releases and patched for security, no new features. Tested by its team with jQuery 1.12.4 up to 4.0.0. |
| DataTables | 3.1.5 | Since version 3 (2026) it no longer needs jQuery, but it still registers itself as a jQuery plugin when jQuery is on the page. |
| Select2, Slick, Chosen and similar | varies | Third-party plugins with their own release cycles. Check each one's repository before upgrading jQuery. |
Two practical lessons from this table. First, old widget versions are a security issue: jQuery UI versions before 1.13.0 have known cross-site scripting (XSS) bugs in options such as the datepicker's text options, and 1.13.2 fixed another, so security scanners flag them. Second, a widget can block your jQuery upgrade: if a plugin calls an API that jQuery 4 removed, the whole page waits for it (see jQuery 3 to 4: What Changed).
The widget life cycle
Every widget, from any library, goes through the same five stages. Most widget bugs come from getting one of them wrong.
- Load the CSS, then jQuery, then the widget's JavaScript. A widget file loaded before jQuery, or a second copy of jQuery loaded after it, gives "is not a function" errors.
- Initialise once on elements that exist:
$("#city").autocomplete({...}). Initialising the same element twice gives duplicate handlers or a warning. - Talk to it through its methods, not by changing its internal HTML:
$("#dlg").dialog("open"),$("#city").autocomplete("option", "minLength", 2). - Listen to its events to react to the user:
selectfor autocomplete,closefor dialogs,drawfor DataTables. - Destroy it before removing or re-rendering its element:
$("#dlg").dialog("destroy"). Removing elements with jQuery's.remove()also destroys jQuery UI widgets automatically.
Here are two jQuery UI widgets going through that life cycle. Type "Pu" into the city box, pick a city, and confirm in the dialog:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>jQuery UI widgets</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/jquery-ui@1.14.2/dist/themes/base/jquery-ui.min.css">
<style>body { font-family: system-ui, sans-serif; }</style>
</head>
<body>
<label for="city">Exam centre city</label>
<input id="city" placeholder="Type Pu or Ja">
<p id="chosen">No centre chosen yet.</p>
<div id="confirm" title="Confirm exam centre">
<p>Book a seat at the <strong id="confirm-city"></strong> centre?</p>
</div>
<!-- Step 1: CSS above, then jQuery, then jQuery UI -->
<script src="https://cdn.jsdelivr.net/npm/jquery@4.0.0/dist/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/jquery-ui@1.14.2/dist/jquery-ui.min.js"></script>
<script>
const centres = ["Pune", "Puducherry", "Purnia", "Jaipur", "Jamshedpur", "Kochi", "Lucknow"];
// Step 2: initialise once
$("#confirm").dialog({
autoOpen: false,
modal: true,
buttons: {
Book: function () {
$("#chosen").text("Seat booked at " + $("#confirm-city").text() + ".");
$(this).dialog("close"); // step 3: call a method
},
Cancel: function () { $(this).dialog("close"); },
},
});
$("#city").autocomplete({
source: centres,
minLength: 2,
select: function (event, ui) { // step 4: react to an event
$("#confirm-city").text(ui.item.value); // .text(), never .html()
$("#confirm").dialog("open");
},
});
</script>
</body>
</html>Notice that the code never touches the HTML that jQuery UI generates (the suggestion list, the dialog's title bar and buttons). That HTML can change between versions; the methods, options and events are the stable, documented interface.
DataTables: search, sort and paging for tables
DataTables turns a plain <table> into a grid with search, sorting and paging. On a jQuery page it is usually started with $("#table").DataTable({...}), which returns an API object for later calls:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>DataTables grid</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/datatables.net-dt@3.1.5/css/dataTables.dataTables.min.css">
<style>body { font-family: system-ui, sans-serif; max-inline-size: 40rem; }</style>
</head>
<body>
<table id="students">
<thead><tr><th>Name</th><th>City</th><th>Marks</th></tr></thead>
<tbody>
<tr><td>Aarav</td><td>Pune</td><td>88</td></tr>
<tr><td>Priya</td><td>Jaipur</td><td>92</td></tr>
<tr><td>Kabir</td><td>Rohtak</td><td>75</td></tr>
<tr><td>Meera</td><td>Kochi</td><td>81</td></tr>
<tr><td>Rohan</td><td>Pune</td><td>67</td></tr>
<tr><td>Ananya</td><td>Lucknow</td><td>95</td></tr>
<tr><td>Farhan</td><td>Jaipur</td><td>79</td></tr>
</tbody>
</table>
<p><button id="add">Add Isha (Kochi, 90)</button> <button id="pune">Show only Pune</button></p>
<p id="info"></p>
<script src="https://cdn.jsdelivr.net/npm/jquery@4.0.0/dist/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/datatables.net@3.1.5/js/dataTables.min.js"></script>
<script>
const table = $("#students").DataTable({
pageLength: 5,
order: [[2, "desc"]], // sort by Marks, highest first
});
// Use the API, not direct DOM edits, so DataTables knows about the change
$("#add").on("click", function () {
table.row.add(["Isha", "Kochi", 90]).draw();
$(this).prop("disabled", true);
});
$("#pune").on("click", function () {
table.search("Pune").draw();
});
table.on("draw", function () {
$("#info").text("Rows matching the search: " + table.rows({ search: "applied" }).count());
});
</script>
</body>
</html>Three facts help when you maintain DataTables code:
- Change data through the API (
row.add(),row().remove(),clear(), thendraw()). If you append a<tr>with jQuery, DataTables does not know it exists and the next sort or search hides it. - Big tables need server-side processing. With options such as
serverSide: trueand anajaxURL, the server does the searching, sorting and paging, and sends one page at a time. Loading 50,000 rows into the browser makes the page freeze. - Version 3 changed callback context. In callbacks such as
rowCallback,thisis no longer a jQuery object but DataTables' own DOM helper. Old code that calls jQuery methods onthismust wrap it with$(this)or use the DataTables API. Without jQuery, the same table is created withnew DataTable("#students", {...}).
Native replacements for new code
Many jQuery UI widgets now have a built-in HTML equivalent. Native elements need no library, work with the phone's own keyboard and pickers, and get accessibility features from the browser:
| jQuery UI widget | Native HTML | Notes |
|---|---|---|
| Datepicker | <input type="date"> | The browser's picker; the value is always YYYY-MM-DD |
| Dialog | <dialog> with showModal() | Focus handling and Escape key built in |
| Autocomplete (fixed list) | <input list="..."> with <datalist> | For server suggestions, combine with a debounced fetch() |
| Tabs, Accordion | <details> (with name for one-open-at-a-time) | Real tabs still need a small script with ARIA roles |
| Tooltip | the popover attribute | Keep important help visible as normal text instead |
| Sortable, Draggable | no direct equivalent | The HTML drag and drop API or a small library |
| DataTables grid | no direct equivalent | Small tables: a short sort/filter script; large ones: server-side paging |
<!-- The same exam-centre form with no library at all -->
<label for="city">Exam centre city</label>
<input id="city" list="centres">
<datalist id="centres">
<option value="Pune"></option><option value="Jaipur"></option><option value="Kochi"></option>
</datalist>
<dialog id="confirm">
<p>Book a seat at the <strong id="confirm-city"></strong> centre?</p>
<form method="dialog"><button value="book">Book</button> <button value="cancel">Cancel</button></form>
</dialog>Common mistakes
- Editing the widget's generated HTML instead of using its options and methods. Your change breaks on the next upgrade.
- Initialising again after every AJAX load without destroying first. Destroy, then re-create, or initialise only the new elements.
- Adding table rows with jQuery on a DataTables table. Use
row.add()anddraw(). - Loading the full jQuery UI bundle for one widget. Custom builds or the native element are much lighter.
- Leaving jQuery UI below 1.13.2 because "it works". Old versions have published XSS vulnerabilities.
Key points
- jQuery UI 1.14.2 is maintenance-only but supports jQuery 4; DataTables 3 no longer needs jQuery but still works as a jQuery plugin.
- Every widget follows the same life cycle: load in order, initialise once, use methods, listen to events, destroy.
- Talk to widgets through their documented options, methods and events, never their internal HTML.
- Change DataTables data through its API, and use server-side processing for large tables.
- For new code, prefer
<input type="date">,<dialog>,<datalist>,<details>andpopover.
Exercise
Using the DataTables example, add a "Remove lowest" button that deletes the student with the lowest marks through the API, and a "Clear search" button. Show the number of rows left in the #info paragraph after every change. Hint: table.rows().data() returns the row data, and table.row(index).remove().draw() removes a row.
Show solution
$("<button>", { text: "Remove lowest" }).appendTo("p:has(#add)").on("click", function () {
let lowestIndex = null;
let lowestMarks = Infinity;
table.rows().every(function (rowIdx) {
const marks = Number(this.data()[2]);
if (marks < lowestMarks) { lowestMarks = marks; lowestIndex = rowIdx; }
});
if (lowestIndex !== null) table.row(lowestIndex).remove().draw();
});
$("<button>", { text: "Clear search" }).appendTo("p:has(#add)").on("click", function () {
table.search("").draw();
});
table.on("draw", function () {
$("#info").text("Rows left: " + table.rows().count()
+ ", matching the search: " + table.rows({ search: "applied" }).count());
});rows().every() visits each row with its index, so the lowest mark can be found from the data, and row(index).remove().draw() removes it through the API. The draw event fires after every change, so the counter stays correct.
Quick Quiz
1. What is the status of jQuery UI in 2026?
The jQuery UI team keeps 1.14.x working with new jQuery versions and fixes security issues only.
2. You add a <tr> to a DataTables table with $("tbody").append(...). After sorting, the row disappears. Why?
DataTables keeps its own copy of the data and redraws the table from it.
3. Which native element replaces the jQuery UI Dialog in new code?
<dialog> with showModal() gives a modal box with focus handling and Escape support built in.
4. What should you do before re-rendering HTML that contains an initialised widget with plain DOM code?
Plain DOM removal skips jQuery's clean-up, so the widget's destroy code would never run.
Learn jQuery with a mentor
Free lessons get you started. Our Full Stack Web Development training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
