loading...
Google Web Dev

Beyond appendChild: Better convenience methods for HTML

samthor profile image Sam Thorogood Updated on ใƒป2 min read

Blog-A-Day in June (19 Part Series)

1) Rebuild only when necessary in Node 2) Civilization is a game you never lose 3 ... 17 3) Arrow functions break JavaScript parsers 4) Detecting Select All on the Web 5) Declaring JS Variables in 2019 6) Sam's dotfiles highlights 7) Automate Reading Form Results with ๐Ÿค– Chrome 8) Beyond appendChild: Better convenience methods for HTML 9) AMA, Sam 10-yr Googler in Web DevRel 10) Disable a HTML form while in-flight using fieldset 11) PWAs that download like apps ๐Ÿ—œ๏ธ 12) Matching elements with selectors in JS 13) Install This PWA To Continue 14) Google Assistant now supports "Open/Close" devices 15) Modern Web Components 16) What To Expect When You're Expecting To Drop IE11 ๐Ÿ—‘๏ธ 17) Divert Vertical Scroll To The Side โ†”๏ธ 18) Graceful Shutdown Is A Lie 19) Progress Indicator With Fetch

I've built on the web for a while now. So long that when I'm writing vanilla HTML/JS, my go-to has always been .appendChild() to add new HTML elements, and a huge series of createElement calls along with it.

But there's actually some lesser-known convenience methods that we can use now (well, in a post-IE11 world, where all devs should be). ๐ŸŒŽ๐Ÿ‘ I'm not arguing against your framework or components, but sometimes, you just have to write vanilla JS. ๐Ÿฆ

One-Line Element Creation

I confess ๐Ÿ˜… that this isn't really one line, but it's one statement:

const el = Object.assign(document.createElement('div'), {
  textContent: `Your div now has text`,
  className: 'and-is-classy',
});

The helper Object.assign wasn't available in IE11.

Remove Self

This one is pretty well-known.

const el = document.getElementById('something');
el.remove();   // instead of el.parentNode.removeChild(el)

Insert Element or Text

The .append() method can append any real elements, or it will automatically create a text node if you pass it a string. It takes any number of arguments.

el.append(document.createElement('hr'), 'I get upgraded to a text node!');

There's also .prepend() which is the opposite of .append(). It inserts all the elements, in-order, at the start of the element:

const heading = Object.assign(document.createElement('h2', {
  textContent: 'List Of Awesome HTML Methods',
});
list.prepend(heading, `You Won't Believe How Many We Found!`);

Insert Relative To Element

Every element has methods .before() and .after(). These insert new HTML nodes directly adjacent to the current node. Like the methods above, they accept any number of other elements or strings.

myHeading.before(superHeading);
myHeading.after(`Here's a list of awesome stuff`, theList);

โš ๏ธ There's one caveat: in our example, if myHeading isn't actually on the pageโ€”it's a temporary elementโ€”these methods will just fail silently without throwing an Error.

Replace Self

Rather than doing a parentNode.replaceChild dance, we can now self-destruct an element and replace it with something new. Again, we can replace ourselves with any number of other elements or strings (even none!).

const fancyItem = Object.assign(document.createElement('strong'), {
  textContent: 'fancy',
});
someFancyHeading.replaceWith('Less', fancyItem, 'heading');
someFancyHeading.replaceWith();  // although you could just use .remove ๐Ÿคท

Class Force Set

If you want to set the state of a class to a variable true or false, you can pass a second param to .classList.toggle:

const someState = false;
theDiv.classList.toggle('foo', !someState);  // forces foo on
theDev.classList.toggle('bar', someState);   // forces bar off
// result e.g. <div id="theDiv" class="foo">

This is probably well-known. But if you're explicitly not supporting IE11, it's nice to be confident that this now works. ๐ŸŽ‰

Done!

What have I missed? Let me know if there any other old habits you've recently discovered you can let go of.

9 ๐Ÿ‘‹

Blog-A-Day in June (19 Part Series)

1) Rebuild only when necessary in Node 2) Civilization is a game you never lose 3 ... 17 3) Arrow functions break JavaScript parsers 4) Detecting Select All on the Web 5) Declaring JS Variables in 2019 6) Sam's dotfiles highlights 7) Automate Reading Form Results with ๐Ÿค– Chrome 8) Beyond appendChild: Better convenience methods for HTML 9) AMA, Sam 10-yr Googler in Web DevRel 10) Disable a HTML form while in-flight using fieldset 11) PWAs that download like apps ๐Ÿ—œ๏ธ 12) Matching elements with selectors in JS 13) Install This PWA To Continue 14) Google Assistant now supports "Open/Close" devices 15) Modern Web Components 16) What To Expect When You're Expecting To Drop IE11 ๐Ÿ—‘๏ธ 17) Divert Vertical Scroll To The Side โ†”๏ธ 18) Graceful Shutdown Is A Lie 19) Progress Indicator With Fetch

Posted on Jun 9 '19 by:

samthor profile

Sam Thorogood

@samthor

Developer Relations for Web at Google.

Google Web Dev

Collected thoughts and posts on web development from the @ChromiumDev team.

Discussion

markdown guide
 

It never occurred to me to create new elements with object assign. ๐Ÿ”ฅ I always created the element then added properties and attributes after.

 

I nearly didn't include this oneโ€”it was added last. But I've seen a few people remark on how neat it is in some of my codebases, so I'm glad to share? ๐Ÿคท

 

It's so useful when adding a bunch of properties to .style ๐Ÿ™‚

Object.assign(el.style, {
 left: x + 'px',
 top: y + 'px'
});

I made a little helper function based on your example @samthor which also handles inline styles. Thought I'd share here in case anyone else finds it useful. So the only thing else I added was handling inline style and wrapped it all into a function.

 

As a curiosity: .remove on <select> elements can take an optional argument. Why?

Because it's the method used to remove an option from its list. So, in this specific case, .remove does two different things: when called with no parameters, removes the element itself; when a index is passed, it removes the corresponding option in the list.

I'd suggest the awkwardly-specified, but still quite useful methods .insertAdjacentElement/.insertAdjacentHTML/.insertAdjacentText, that originated with Internet Explorer 4 (!), and take two arguments. The first of which is a string among 'beforebegin', 'beforeend', 'afterbegin' and 'afterend'. Weird.

But anyway, while the other two can be nicely polyfilled with other methods, .insertAdjacentHTML is quite unique, and can be seen as the only correct way to append a piece of HTML to an element without too much fuss or making a mess (someone said el.innerHTML += '<b>Just kidding!</b>'?).

 

I've not really used the .insertAdjacent... methods. Good point about adding HTML thoughโ€”I suppose the other way to do that is to create a dummy node (or fragment) and then append the content of that, but that is nice... if you're adding HTML directly.

 

Hi Sam,

For some reasons I stumbled upon your article while googling and if I may ask, how would you write vanilla JS for this

    $(".accordion-title").prepend(
        '<span class="toggle"><span></span><span></span></span>'
    );

Your input is much appreciated ;)

 

For a one-liner... hmm:

document.querySelector('.accordion-title').prepend(
  Object.assign(document.createElement('span'), {className: 'toggle', innerHTML: '<span></span><span></span>'}),
);

If you're not happy using innerHTML then it could look like this:

const toggleEl = Object.assign(document.createElement('span'), {class: 'toggle'});
toggleEl.append(document.createElement('span'), document.createElement('span'));
document.querySelector('.accordion-title').prepend(toggleEl);
 

I keep coming back to this because I love it and am using the object assign right now, I have a little question.

Is there a way in object assign to insert children such as for instance if I want to put a x next to a image to say delete?

 

I don't think so, sorry. You can't assign to .children in this way.

 

Thanks for your reply, I was playing with it a little bit probably when I should have been concentrating on work ๐Ÿ˜‚.
You can use innerHTML though!! And I called the on click with a function so learned 2 new things yesterday about it

 

Object.assign works wonderfully~~~

demo

 

const el = Object.assign(document.createElement('div'), {
textContent: Your div now has text,
className: 'and-is-classy',
});

This is sooooo gooood!!!

 

I love this and will be playing with it today!!