Three ways to create an element
Passing HTML to $() creates new elements instead of searching for existing ones. jQuery recognises HTML because the string starts with <.
<div id="out"></div>
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script>
// 1. An empty element, then chain setters
const $p = $("<p>").text("Created empty, then filled.").addClass("note");
// 2. A full HTML string
const $box = $("<div class='box'><strong>Bold</strong> text</div>");
// 3. A tag plus an object of properties
const $link = $("<a>", { href: "/learn/", text: "All courses", title: "Browse courses" });
console.log("in the page yet?", $.contains(document.body, $p[0]));
$("#out").append($p, $box, $link);
console.log("in the page now?", $.contains(document.body, $p[0]));
console.log($("#out").html());
</script>in the page yet? false
in the page now? true
<p class="note">Created empty, then filled.</p><div class="box"><strong>Bold</strong> text</div><a href="/learn/" title="Browse courses">All courses</a>A new element exists only in memory until you insert it; the first log line shows it is not part of the page yet. The append() call, explained in the next lesson, puts it in place.
The properties object
The third form, $("<tag>", { ... }), is the clearest when an element needs several things set. Each key in the object is applied for you:
textandhtmlset the content (with.text()or.html()).classsets the class attribute (quote it or write"class", sinceclassis a reserved word in some contexts).- Any key that is a jQuery method, such as
cssordata, calls that method. - Any key that is an event name, such as
clickorinput, attaches a handler. - Everything else becomes an attribute.
<div id="app"></div>
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script>
const $button = $("<button>", {
type: "button",
text: "Say hello",
"class": "btn primary",
css: { padding: "6px 12px" },
data: { greeting: "Hello from jQuery" },
click: function () {
console.log($(this).data("greeting"));
}
});
$("#app").append($button);
console.log($button.attr("class"), "|", $button.attr("type"));
</script>btn primary | button
Hello from jQueryOne expression created a button with its type, label, classes, inline style, stored data and a click handler.
Safe content from users
Gluing values into an HTML string is the easiest way to create an XSS hole:
<ul id="list"></ul>
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script>
const name = '<img src=x onerror="console.log(\'unsafe code ran\')">';
// Unsafe: the name becomes markup
$("#list").append("<li>" + name + "</li>");
// Safe: the name becomes text
$("#list").append($("<li>", { text: name }));
</script>unsafe code ranThe first item ran the attacker's code; the second showed the name as characters. Any time a value comes from a form, a URL or a server, create the element and set the value as text.
Building a list from data
Here is a realistic task: turn an array of objects into a table.
<table id="people" border="1" cellpadding="4">
<thead><tr><th>Name</th><th>City</th></tr></thead>
<tbody></tbody>
</table>
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script>
const people = [
{ name: "Asha Verma", city: "Pune" },
{ name: "Ben Thomas", city: "Kochi" },
{ name: "Chen Li", city: "Jaipur" }
];
const rows = people.map(person =>
$("<tr>").append(
$("<td>", { text: person.name }),
$("<td>", { text: person.city })
)
);
$("#people tbody").append(rows);
console.log($("#people tbody tr").length, "rows added");
</script>3 rows addedThe rows are built in a plain array with map(), then added with one append() call. append() accepts an array of jQuery objects or elements.
Why insert once?
Each time you insert something into the page, the browser may need to recalculate the layout. For three rows it does not matter; for a few hundred it can make the page stutter. Building everything first and inserting it once is the simplest performance habit in jQuery. The performance lesson later measures the difference.
Creating from a template
For bigger pieces of HTML, a <template> element in the page keeps the markup in HTML, where it belongs, and your code clones it:
<template id="card-tpl">
<div class="card"><h4 class="title"></h4><p class="body"></p></div>
</template>
<div id="cards"></div>
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script>
const tpl = $("#card-tpl").prop("content");
const $card = $(tpl).children().first().clone();
$card.find(".title").text("Welcome");
$card.find(".body").text("Cards are cloned from a template.");
$("#cards").append($card);
console.log($("#cards .card").length, $("#cards .title").text());
</script>1 Welcome.prop("content") gives the template's contents, and .clone() (covered in the wrapping lesson) makes a fresh copy each time.
Common mistakes
- Creating an element and forgetting to insert it, then wondering why nothing appears.
- Concatenating user data into HTML strings.
- Appending inside a loop for large lists instead of collecting and appending once.
- Writing invalid HTML strings, such as
$("<li>text"); jQuery may fix it, but write complete tags or use the properties object.
Exercise
Given an array of three links (each with label and url), create an a element for each, inside an li, and append them all to a ul in one call.
Show one possible answer
<ul id="nav"></ul>
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script>
const links = [
{ label: "Home", url: "/" },
{ label: "Learn", url: "/learn/" },
{ label: "Tools", url: "/tools/" }
];
const items = links.map(link => $("<li>").append($("<a>", { href: link.url, text: link.label })));
$("#nav").append(items);
console.log($("#nav a").length);
</script>3Key points
- $("<li>") creates a new, empty element; it exists in memory until you insert it.
- $("<a>", { href, text, class }) creates an element and sets its text, attributes and even event handlers in one call.
- Set user-supplied content with text, never by gluing it into an HTML string.
- Build all the new elements first and insert them in one go for speed.