Navigation Menu

Skip to content

m3g4p0p/from-html

Repository files navigation

from-html

JavaScript Style Guide Coverage 100%

A utility function to get element references directly from a HTML string.

Why?

Creating nested DOM elements with JS can be tedious and verbose; you either have to create and assemble them manually, or set the .innerHTML of a container element and then query for its children so that you can add event listeners etc. With fromHTML() you can do both in one go:

const {
  modal,
  cancelBtn,
  confirmBtn
} = fromHTML(`
  <div ref="modal" class="modal__overlay">
    <div class="modal__container">
      <div class="modal__content">Some message</div>
      <button
        ref="cancelBtn"
        class="modal__cancel-btn"
      >Cancel</button>
      <button
        ref="confirmBtn"
        class="modal__confirm-btn"
      >Confirm</button>
    </div>
  </div>
`)

cancelBtn.addEventListener('click', /* ... */)
confirmBtn.addEventListener('click', /* ... */)
document.body.appendChild(modal)

Or actually add event listeners directly:

const { modal } = fromHTML(`
  <div ref="modal" class="modal__overlay">
    <div class="modal__container">
      <div class="modal__content">This site uses cookies.</div>
      <button
        on="click:accept"
        class="modal__confirm-btn"
      >Accept</button>
      <button
        on="click:reject"
        class="modal__cancel-btn"
      >Reject</button>
    </div>
  </div>
`, {
  accept () {
    document.cookie = 'cookies_accepted=1'
    modal.style.display = 'none'
  },
  reject () {
    throw 'We gotta get out of this place!'
  }
})

Installation

Install as usual:

yarn add from-html

And in your JS:

import fromHTML from 'from-html'

The script can also be downloaded or directly included from unpkg.com:

<script src="https://unpkg.com/from-html"></script>

Usage

fromHTML(htmlString [, controller [, options]])

References

The values of the ref attributes will get mapped to the property names of the returned object; you can also get an array of elements (not a node list!) by appending square brackets to the ref name:

const names = ['Jane', 'John', 'Jimmy']

const { list, items } = fromHTML(`
  <ul ref="list">
    ${names.map(name => `<li ref="items[]">${name}</li>`).join('')}
  </ul>
`)

Instead of a HTML string it's also possible to pass an ID selector of a template to use:

<script type="text/template" id="my-template">
  <ul ref="list">
    <li ref="items[]">Jane</li>
    <li ref="items[]">John</li>
    <li ref="items[]">Jimmy</li>
  </ul>
</script>
const { list, items } = fromHTML('#my-template')

Events

While at it, you can also add event listeners by providing a controller object and binding its methods with on attributes:

const { button } = fromHTML(`
  <button ref="button" on="click:sayHello">Click me!</button>
`, {
  sayHello () {
    window.alert('Hello HTML!')
  }
})

The part before the colon specifies the type of the event, the part after it the method of the controller to call. Multiple events can be bound with a space-separated list:

const { button } = fromHTML(`
  <button
    ref="button"
    on="mousedown:sayHello mouseup:sayGoodbye"
  >Click me!</button>
`, {
  sayHello () {
    window.alert('Hello HTML!')
  },
  sayGoodbye () {
    throw 'Goodbye!'
  }
})

If the method name is omitted, the controller object itself will be used to handle events (assuming of course it implements the EventListener interface):

const { button } = fromHTML(`
  <button ref="button" on="mousedown mouseup">Click me!</button>
`, {
  handleEvent ({ type }) {
    switch (type) {
      case 'mousedown':
        window.alert('Hello HTML!')
        break
      case 'mouseup':
        throw 'Goodbye!'
    }
  }
})

Options

The following options can be specified:

Name Type Default Description
refAttribute string ref The attribute to get the element references from
eventAttribute string on The attribute denoting event bindings
removeRefAttribute boolean true Whether to remove the reference attribute afterwards
removeEventAttribute boolean true Whether to remove the event attribute afterwards
assignToController boolean|string false Whether to assign the element references to the controller, or to a given property of the controller if a string is provided

For example, if you want to keep the ref attribute you might use data-* attributes for HTML compliance:

const { button } = fromHTML(`
  <button data-ref="button">Click me!</button>
`, null, {
  refAttribute: 'data-ref',
  removeRefAttribute: false
})

Assigning to the controller

Instead of an options object you can also pass a boolean as a shorthand for assignToController:

class DisposableButton {
  constructor (text) {
    fromHTML(`
      <button ref="_el" on="click">${text}</button>
    `, this, true)
  }

  mount (target) {
    target.append(this._el)
  }

  handleEvent ({ type }) {
    if (type === 'click') {
      this._el.remove()
    }
  }
}

It is also possible to pass a string to specify a property to which the references should get assigned:

class SwitchButton {
  constructor () {
    fromHTML(`
      <span ref="container">
        <button ref="onBtn" on="click">on</button>
        <button ref="offBtn" on="click" hidden>off</button>
      </span>
    `, this, 'refs')
  }

  mount (target) {
    target.append(this.refs.container)
  }

  handleEvent ({ type }) {
    const toggleHidden = el => {
      el.hidden = !el.hidden
    }

    if (type === 'click') {
      toggleHidden(this.refs.onBtn)
      toggleHidden(this.refs.offBtn)
    }
  }
}

In both cases the object to which the references got assigned will be returned (i.e. the controller itself or its specified property).

License

MIT @ m3g4p0p 2018

About

Get element references directly from a HTML string

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published