When are the constructor and connectedCallback methods called when creating a Custom Element?
Tags: webcomponents, vanillajs
Published: 2021-03-18 22:55:24

---
title: When are the constructor and connectedCallback methods called when creating a Custom Element?
published: true
date: 2021-03-15 00:00:00 UTC
tags: webcomponents,vanillajs
canonical_url: https://lamplightdev.com/blog/2021/03/15/when-are-the-constructor-and-connectedcallback-methods-called-when-creating-a-custom-element/
---

The `constructor` and `connectedCallback` lifecycle methods of Custom Elements are called when your element is created and attached to the DOM respectively, but there are a few subtleties to keep in mind depending on how and when you define, create, insert and declare your element.

So how are Custom Elements defined, created, inserted and declared? And at which stage do the lifecycle methods get called?

### Define

A custom element is **defined** when `customElements.define` is called:

```js
class MyElement extends HTMLElement {
	constructor() {
		super();

		console.log('constructed');
	}

	connectedCallback() {
		console.log('connected');
	}
}

customElements.define('my-element', MyElement);
```

Defining an element **doesn't** trigger either the `constructor` or the `connectedCallback` methods since it does not create an instance of an element

An element can only be defined once.

### Create

An element can be **created** in JavaScript in two ways:

```js
// can happen before definition
const myElement = document.createElement('my-element');
```
```js
// can only happen if already defined
const myElement = new MyElement();
```

Creation triggers the `constructor`, **if** the element has already been defined.

The `constructor` is only ever called once per element instance.

### Insert

An element is **inserted** into the DOM imperatively with JS:

```js
document.body.append(myElement);
```

Insertion triggers the `connectedCallback` method, **if** the element has already been defined.

`connectedCallback` is called each time the element is inserted into the DOM, with the `disconnectedCallback` method called each time it is removed from the DOM.

### Declare

An element is **declared** when parsed as HTML:

```html
<my-element></my-element>
```
```js
document.body.innerHTML = '<my-element></my-element>';
```

Declaration triggers the `constructor` and `connectedCallback` methods, **if** the element has already been defined.

### Upgrade

In all the above cases the lifecycle methods are only called if the element has already been defined. An element is **upgraded** when it already exists before definition - at the point of definition the `constructor` is then called automatically. If the element was already attached to the DOM at this point `connectedCallback` will also be called.

## Examples

The examples below cover the different orders of the above stages to illustrate when the lifecycle methods are called.

### Define then Declare

```html
<html>
	<head>
		<script>
			class MyElement extends HTMLElement {
				constructor() {
					super();

					console.log('constructed');
				}

				connectedCallback() {
					console.log('connected');
				}
			}

			customElements.define('my-element', MyElement);
		</script>
	</head>
	<body>
		<my-element></my-element>
		<!-- `constructor` then `connectedCallback` are called here when the element has been parsed -->
	</body>
</html>
```

### Declare then Define

```html
<html>
	<head>
	</head>
	<body>
		<my-element></my-element>

		<script>
			class MyElement extends HTMLElement {
				constructor() {
					super();

					console.log('constructed');
				}

				connectedCallback() {
					console.log('connected');
				}
			}

			// UPGRADE
			customElements.define('my-element', MyElement);
			/**
			`constructor` then `connectedCallback` are called here when the element has been defined -->
			**/
		</script>
	</body>
</html>
```

### Define then Create then Insert

```html
<html>
	<head>
	</head>
	<body>
		<script>
			class MyElement extends HTMLElement {
				constructor() {
					super();

					console.log('constructed');
				}

				connectedCallback() {
					console.log('connected');
				}
			}

			customElements.define('my-element', MyElement);

			const myElement = document.createElement('my-element');
			/**
			`constructor` called here when element is created
			**/

			document.body.appendChild(myElement);
			/**
			`connectedCallback` called here when element is inserted
			**/
		</script>
	</body>
</html>
```

### Create then Insert then Define

```html
<html>
	<head>
	</head>
	<body>
		<script>
			const myElement = document.createElement('my-element');

			document.body.appendChild(myElement);

			class MyElement extends HTMLElement {
				constructor() {
					super();

					console.log('constructed');
				}

				connectedCallback() {
					console.log('connected');
				}
			}

			// UPGRADE
			customElements.define('my-element', MyElement);
			/**
			`constructor` then `connectedCallback` are called here when the element has been defined -->
			**/
		</script>
	</body>
</html>
```

### Create then Define then Insert

```html
<html>
	<head>
	</head>
	<body>
		<script>
			const myElement = document.createElement('my-element');

			class MyElement extends HTMLElement {
				constructor() {
					super();

					console.log('constructed');
				}

				connectedCallback() {
					console.log('connected');
				}
			}

			// UPGRADE
			customElements.define('my-element', MyElement);
			/**
			`constructor` called here when the element has been defined
			**/

			document.body.appendChild(myElement);
			/**
			`connectedCallback` called here when the element has been defined -->
			**/
		</script>
	</body>
</html>
```

## Why does any of this matter?

Knowing when, how and why the `constructor` and `connectedCallback` methods are called is important when initialising your Custom Elements. Generally:

- the `constructor` is best suited to setting up initial state and events that don't need to be removed or cleaned up when the element is destroyed (as there is no `deconstructor` method.)

- the `constructor` is only ever called once per element so initialisation that needs to happen each time the element is attached to the DOM should be deferred to `connectedCallback`.

- the `connectedCallback` method is best suited to most other initialisation tasks. Any clean up can then happen in `disconnectedCallback`.

- attributes and child elements should not be accessed or modified in the `constructor` since, depending on how the element is created, they may not exist e.g. `document.createElement('my-element');` - this will trigger the `constructor` but the element has had no chance yet to set attributes or children.

- if you need to access / modify attributes or children on initialisation this must therefore happen in `connectedCallback`. In this case you will often need to guard against such initialisation happening multiple times as the element is removed and re-attached to the DOM.

[Subscribe to my mailing list to be notified of new posts about Web Components and building performant websites](http://eepurl.com/gvjFwv)