Skip to content

Instantly share code, notes, and snippets.

@briannorman
Created July 16, 2024 19:28
Show Gist options
  • Save briannorman/dcda0e201fd1905036d085a8d713f1f7 to your computer and use it in GitHub Desktop.
Save briannorman/dcda0e201fd1905036d085a8d713f1f7 to your computer and use it in GitHub Desktop.
Mutate Examples

Mutate Library

Example HTML

All of the code examples below reference the following basic HTML:

<!-- Demo Site -->
<!-- https://f6yx6z.csb.app/ -->

<html>
  <head>
    <!-- The Evolv snippet must be loaded before the mutate library can be used -->
    <script
      src="https://media.evolv.ai/asset-manager/releases/latest/webloader.min.js"
      data-evolv-environment="c1325716de"
      data-evolv-pushstate="true"
    ></script>
    <link
      rel="stylesheet"
      href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css"
    />
    <link rel="stylesheet" href="style.css" />
  </head>
  <body>
    <nav>
      <ul>
        <li class="navItemOne styleOne styleTwo styleThree">nav item 1</li>
        <li class="navItemTwo">nav item 2</li>
        <li>nav item 3</li>
        <li>nav item 4</li>
      </ul>
    </nav>
    <main>
      <div class="section section-one">
        <h1>Section One</h1>
        <div class="content">
          <h2>Yar Pirate Ipsum</h2>
          <p>
            Prow scuttle parrel provost Sail ho shrouds spirits boom mizzenmast
            yardarm. Pinnace holystone mizzenmast quarter crow's nest nipperkin
            grog yardarm hempen halter furl. Swab barque interloper chantey
            doubloon starboard grog black jack gangway rutters.
          </p>
        </div>
      </div>
      <div class="section section-two">
        <h1>Section Two</h1>
        <div class="content">
          <img
            src="https://pirateipsum.me/img/poster1.jpg"
            alt="pirate ipsum poster 1"
          />
          <img
            src="https://pirateipsum.me/img/poster2.jpg"
            alt="pirate ipsum poster 2"
          />
        </div>
      </div>
      <div class="section section-three">
        <h1>Section Three</h1>
        <div class="content">
          <a href="https://evolv.ai/">Evolv AI</a>
        </div>
      </div>
    </main>
  </body>
</html>

Rule: Before you are able to mutate an element(s), you must first collect the element(s).

Collect

In order to mutate an element or elements on the page, you need to first collect the element(s);

The collect method requires two parameters:

  1. The selector of the element(s)
  2. The name that you would like to use to refer to the collector. This name can be whatever you'd like.
var collect = window.evolv.collect;
var mutate = window.evolv.mutate;

collect("nav > ul", "navList");
collect("nav > ul > li", "navItem");
collect(".navItemOne", "navItemOne");
collect(".section-one", "sectionOne");
collect(".section-one p", "sectionOneCopy");
collect(".section-two img", "sectionTwoImage");
collect(".section-three a", "sectionThreeLink");

In the example above, the following number of elements will be collected for each of the following collector names:

  • navList - 1
  • navItem - 4
  • navItemOne - 1
  • sectionOneCopy - 1
  • sectionTwoImage - 2
  • sectionThreeLink - 1

Once an element has been collected, the element will have a class added to it that prefixes the collector name with mutate-, thus all of navItem elements will have the class mutate-navItem added.

Subscribe

The subscribe method behaves like a mutationObserver allowing you to run a callback method when a collected component is added, modified, or removed from the page.

collect.get('sectionTwoImage').subscribe((action, sectionTwoImage) => {
  switch (action) {
  case 0:
    console.log('sectionTwoImage has been collected and exists in the DOM');
    break;
  case 1:
    console.log('sectionTwoImage has been removed the DOM');
    break;
  case 2:
    console.log('sectionTwoImage has been modified');
    break;
  default:
    // action will always be 0, 1, or 2.
  }
});

Mutate

Custom Mutation

A customMutation gives you a reference to each element collected and allows you to manipulate the element with standarad JavaScript.

mutate('sectionOneCopy').customMutation((state, sectionOneCopy) => {
  // log the element collected for sectionOneCopy
  console.log('sectionOneCopy: ', sectionOneCopy);

  // change the textContent of the element
  sectionOneCopy.textContent = "this is my new copy";

  // add an attribute to the element
  sectionOneCopy.setAttribute('myNewAttribute', 'my new attribute value');
});

.on()

The .on() method is a shorthand way to add an event listener to the element(s) collected.

mutate('navItem').on('click', (e) => {
  console.log(`${e.target.textContent} clicked!`);
});

.attributes()

The .attributes() method allows you to manipulate existing element attributes or add a new attributes. Notice that both images will change since the collector is looking at the selector .section-two img. If we want to change only the first image, we'd need to collect only the first image by making the selector more specific.

mutate('sectionTwoImage').attributes({
  'src': 'https://picsum.photos/200/300',
  'class': 'cheeseburger'
});

.text()

The .text() method will update the text of the component that you are mutating.

mutate('navItemOne').text('updated text');

.html()

The .html() method allows you to replace the innerHTML of the element mutated with the HTML that you pass into the method.

mutate('sectionOneCopy').html(`
  <ul>
    <li>I</li>
    <li>can</li>
    <li>replace</li>
    <li>the</li>
    <li>existing</li>
    <li>HTML</li>
    <li>with</li>
    <li>anything</li>
    <li>I</li>
    <li>want!</li>
  </ul>
`);

.classes()

The .classes() method adds or removes classes from a collected component. In the following example, classOne will be removed from the first navItem component and classFour will be added to all of the navItem components.

mutate('navItem').classes({
  'classOne': false,
  'classFour': true
});

.styles()

The .styles() method adds or updates inline styles for a collected component. In the following example, color: blue; will be added as an inline style to all navItem components.

mutate('navItem').styles({
  'color': 'blue'
});

.hide()

The .hide() method will set an inline style on the element(s) collected to display: none.

mutate('sectionOneCopy').hide();

.child()

The .child() method takes a selector and finds the first child element that matches the selector.

In the following example, the class sectionOneTitle will be added to the <h1> element in sectionOne.

mutate("navList")
  .child("li")
  .classes({
    'firstNavItem': true
  });

.children()

The .children() method gives a reference to all of the immediate child elements.

In the following example, the class sectionOneChild will be added to the h1 and div children in sectionOne.

mutate("sectionOne")
  .children()
  .classes({
    'sectionOneChild': true
  });

.parent()

The .parent() method takes a selector and finds the first ancestor element that matches the selector.

In the following example, the class sectionOneCopyAncestor will be added to the <main> element.

mutate("sectionOneCopy")
  .parent('main')
  .classes({
    'sectionOneCopyAncestor': true
  });

.parents()

The .parents() method gives a reference to all of the ancestor elements.

In the following example, the class sectionOneChild will be added to the h1 and div children in sectionOne.

mutate("sectionOne")
  .parents()
  .classes({
    'sectionOneAncestor': true
  });

.inject()

The inject() method will insert HTML or an element next to the element being mutated. Essentially, it is a shorthand method for insertAdjacentHTML or insertAdjacentElement.

Arguments The .inject method takes in two arguments, where the second argument is optional and defaults to true.

  1. an HTML string or element reference.
  2. a boolean value for whether or not the HTML element created/element reference should be cloned.

Placement Options

  • .before() - behaves like beforebegin
  • .prepend() - behaves like afterbegin
  • .append() - behaves like beforeend
  • .after() - behaves like afterend

In the following HTML example, a new <li> will be inserted before navItemOne.

mutate('navItemOne').inject(`
    <li class="navItemZero">nav item 0</li>
`).before();

In the following Element example, navItemTwo will be placed before navItemOne.

mutate("navItemOne")
  .inject(document.querySelector(".navItemTwo"), false)
  .before();

In the following Element example, navItemTwo will be cloned and the clone will be placed before navItemOne. This results in 2 navItemTwo elements.

mutate("navItemOne")
  .inject(document.querySelector(".navItemTwo"), true)
  .before();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment