Skip to main content
Since Shoelace 2.0 Code stable

Mutation Observer

<sl-mutation-observer> | SlMutationObserver

The Mutation Observer component offers a thin, declarative interface to the MutationObserver API.

Examples

Mutation Observer Basics

The mutation observer will report changes to the content it wraps through the sl-mutation event. When emitted, a collection of MutationRecord objects will be attached to event.detail that contains information about how it changed.

Click to mutate
👆 Click the button and watch the console
<div class="mutation-overview">
  <sl-mutation-observer attr="variant">
    <sl-button variant="primary">Click to mutate</sl-button>
  </sl-mutation-observer>

  <br />
  👆 Click the button and watch the console

  <script>
    const container = document.querySelector('.mutation-overview');
    const mutationObserver = container.querySelector('sl-mutation-observer');
    const button = container.querySelector('sl-button');
    const variants = ['primary', 'success', 'neutral', 'warning', 'danger'];
    let clicks = 0;

    // Change the button's variant attribute
    button.addEventListener('click', () => {
      clicks++;
      button.setAttribute('variant', variants[clicks % variants.length]);
    });

    // Log mutations
    mutationObserver.addEventListener('sl-mutation', event => {
      console.log(event.detail);
    });
  </script>

  <style>
    .mutation-overview sl-button {
      margin-bottom: 1rem;
    }
  </style>
</div>
.mutation-overview
  sl-mutation-observer attr="variant"
    sl-button variant="primary"
      | Click to mutate
  br
  |  👆 Click the button and watch the console

javascript:
  const container = document.querySelector('.mutation-overview');
  const mutationObserver = container.querySelector('sl-mutation-observer');
  const button = container.querySelector('sl-button');
  const variants = ['primary', 'success', 'neutral', 'warning', 'danger'];
  let clicks = 0;

  // Change the button's variant attribute
  button.addEventListener('click', () => {
    clicks++;
    button.setAttribute('variant', variants[clicks % variants.length]);
  });

  // Log mutations
  mutationObserver.addEventListener('sl-mutation', event => {
    console.log(event.detail);
  });

css:
  .mutation-overview sl-button {
    margin-bottom: 1rem;
  }
import { useState } from 'react';
import SlButton from '@teamshares/shoelace/dist/react/button';
import SlMutationObserver from '@teamshares/shoelace/dist/react/mutation-observer';

const css = `
  .resize-observer-overview div {
    display: flex;
    border: solid 2px var(--sl-input-border-color);
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: 4rem 2rem;
  }
`;

const variants = ['primary', 'success', 'neutral', 'warning', 'danger'];
let clicks = 0;

const App = () => {
  const [variant, setVariant] = useState('primary');

  function handleClick() {
    clicks++;
    setVariant(variants[clicks % variants.length]);
  }

  return (
    <>
      <SlMutationObserver attr="*" onSlMutation={event => console.log(event.detail)}>
        <SlButton variant={variant} onClick={handleClick}>
          Click to mutate
        </SlButton>
      </SlMutationObserver>

      <style>{css}</style>
    </>
  );
};

Child List

Use the child-list attribute to watch for new child elements that are added or removed.

Add button
👆 Add and remove buttons and watch the console
<div class="mutation-child-list">
  <sl-mutation-observer child-list>
    <div class="buttons">
      <sl-button variant="primary">Add button</sl-button>
    </div>
  </sl-mutation-observer>

  👆 Add and remove buttons and watch the console

  <script>
    const container = document.querySelector('.mutation-child-list');
    const mutationObserver = container.querySelector('sl-mutation-observer');
    const buttons = container.querySelector('.buttons');
    const button = container.querySelector('sl-button[variant="primary"]');
    let i = 0;

    // Add a button
    button.addEventListener('click', () => {
      const button = document.createElement('sl-button');
      button.textContent = ++i;
      buttons.append(button);
    });

    // Remove a button
    buttons.addEventListener('click', event => {
      const target = event.target.closest('sl-button:not([variant="primary"])');
      event.stopPropagation();

      if (target) {
        target.remove();
      }
    });

    // Log mutations
    mutationObserver.addEventListener('sl-mutation', event => {
      console.log(event.detail);
    });
  </script>

  <style>
    .mutation-child-list .buttons {
      display: flex;
      gap: 0.25rem;
      flex-wrap: wrap;
      margin-bottom: 1rem;
    }
  </style>
</div>
.mutation-child-list
  sl-mutation-observer child-list="true"
    .buttons
      sl-button variant="primary"
        | Add button

javascript:
  const container = document.querySelector('.mutation-child-list');
  const mutationObserver = container.querySelector('sl-mutation-observer');
  const buttons = container.querySelector('.buttons');
  const button = container.querySelector('sl-button[variant="primary"]');
  let i = 0;

  // Add a button
  button.addEventListener('click', () => {
    const button = document.createElement('sl-button');
    button.textContent = ++i;
    buttons.append(button);
  });

  // Remove a button
  buttons.addEventListener('click', event => {
    const target = event.target.closest('sl-button:not([variant="primary"])');
    event.stopPropagation();

    if (target) {
      target.remove();
    }
  });

  // Log mutations
  mutationObserver.addEventListener('sl-mutation', event => {
    console.log(event.detail);
  });

css:
  .mutation-child-list .buttons {
    display: flex;
    gap: 0.25rem;
    flex-wrap: wrap;
    margin-bottom: 1rem;
  }
import { useState } from 'react';
import SlButton from '@teamshares/shoelace/dist/react/button';
import SlMutationObserver from '@teamshares/shoelace/dist/react/mutation-observer';

const css = `
  .mutation-child-list .buttons {
    display: flex;
    gap: .25rem;
    flex-wrap: wrap;
    margin-bottom: 1rem;
  }
`;

let buttonCount = 0;

const App = () => {
  const [buttonIds, setButtonIds] = useState([]);

  function addButton() {
    setButtonIds([...buttonIds, ++buttonCount]);
  }

  function removeButton(id) {
    setButtonIds(buttonIds.filter(i => i !== id));
  }

  return (
    <>
      <div className="mutation-child-list">
        <SlMutationObserver child-list onSlMutation={event => console.log(event.detail)}>
          <div className="buttons">
            <SlButton variant="primary" onClick={addButton}>
              Add button
            </SlButton>
            {buttonIds.map(id => (
              <SlButton key={id} variant="default" onClick={() => removeButton(id)}>
                {id}
              </SlButton>
            ))}
          </div>
        </SlMutationObserver>
      </div>
      👆 Add and remove buttons and watch the console
      <style>{css}</style>
    </>
  );
};

Importing

If you’re using the autoloader or the traditional loader, you can ignore this section. Otherwise, feel free to use any of the following snippets to cherry pick this component.

Script Import Bundler React

To import this component from the CDN using a script tag:

<script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.1.0/cdn/components/mutation-observer/mutation-observer.js"></script>

To import this component from the CDN using a JavaScript import:

import 'https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.1.0/cdn/components/mutation-observer/mutation-observer.js';

To import this component using a bundler:

import '@shoelace-style/shoelace/dist/components/mutation-observer/mutation-observer.js';

To import this component as a React component:

import SlMutationObserver from '@shoelace-style/shoelace/dist/react/mutation-observer';

Slots

Name Description
(default) The content to watch for mutations.

Learn more about using slots.

Properties

Name Description Reflects Type Default
attr Watches for changes to attributes. To watch only specific attributes, separate them by a space, e.g. attr="class id title". To watch all attributes, use *. string -
attrOldValue
attr-old-value
Indicates whether or not the attribute’s previous value should be recorded when monitoring changes. boolean false
charData
char-data
Watches for changes to the character data contained within the node. boolean false
charDataOldValue
char-data-old-value
Indicates whether or not the previous value of the node’s text should be recorded. boolean false
childList
child-list
Watches for the addition or removal of new child nodes. boolean false
disabled Disables the observer. boolean false
updateComplete A read-only promise that resolves when the component has finished updating.

Learn more about attributes and properties.

Events

Name React Event Description Event Detail
sl-mutation onSlMutation Emitted when a mutation occurs. { mutationList: MutationRecord[] }

Learn more about events.