Why custom elements not support attributes as an object?

1.1k views Asked by At

I am trying to pass data attribute in custom element as an object but while receiving inside attachedCallback method getting value "[object object]" in a string form.

So can anyone help me to figure out what is the work around to get the attributes as an object inside custom-element(web component).

code sample

<script>
class myElements extends HTMLElement {
    createdCallback() {
        this.innerHTML = `<h1>Hello</h1>`;
    }
    attachedCallback() {
        console.log(this.getAttribute('data'));
    }
}

 document.registerElement('my-element', myElements);
</script>

custom element tag

<script>
    var object = { key1: 111, key2: 222, key3: function(){return "hi"}, key4:[1,2,3]};

   function changeHandler() {
    page('/default', function() {
        // some logic to decide which route to redirect to
        if (admin) {
            page.redirect('/admin');
        } else {
            page.redirect('/guest');
        }
    });
}

</script>

<my-element data="object" onchange="changeHandler"></my-element>

Note: suppose that <my-element> is a dropdown which gives user option to choose some value.

Solution: Still no native solution in custom-element specs(v0 and v1).

Since Custom Elements doesn't support data binding so we need a sugaring layer for that (e.g., Polymer or SkateJS) as mention by @tony in the comment.

2

There are 2 answers

2
Himanshu Tanwar On

Try by converting object to JSON string,

var object = { key1: 111, key2: 222};
JSON.stringify(object);

Then when you want to get the value, parse it back to object

JSON.parse(this.getAttribute('data'));
0
Supersharp On

Custom Elements does not modify the standard HTML element attribute behaviour which is always of type string.

Because of that, you shoud instead either:

  1. Send a change Event from your custom element that will trigger the onchange handler.

  2. Register you object/function callback via a custom element method.

  3. Modify a state attribute that will be observed (with Mutation Observer) by its container.

If you want to use attribute anyways you can always use eval().


Example with solution 1 with call to changeHandler():

//define custom element
class DropDown extends HTMLElement 
{
  connectedCallback ()
  {
    var span = this.querySelector( 'span' )

    //define ul list
    var ul = this.querySelector( 'ul' )

    ul.onclick = ev => {
      if ( this.value != ev.target.textContent )
      {
        this.value = ev.target.textContent
        this.setAttribute( 'value', this.value )
        span.textContent = this.value 
        this.dispatchEvent( new CustomEvent( 'change' ) )
      }
    }

    //show or hide
    this.onclick = ev => ul.classList.toggle( 'show' )
    
  }

}
customElements.define( 'drop-down', DropDown )
drop-down  {
  position: relative ;
  cursor: pointer ;
  display: inline-block ;
}
drop-down > span {
  border: 1px solid #aae ;
  padding: 2px 5px ;
}
drop-down > ul {
  position: absolute ;
  top: 4px ; left: 5px ;
  list-style: none ;
  outline: 1px solid #aae ;
  padding: 0 ;
  display: inline-block ;
  opacity: 0 ;
  transition: all 0.3s ease-out ;
  background: white ;
  visibility: hidden ;
  z-index: 10 ;
}
drop-down > ul.show {
  opacity: 1 ;
  visibility: visible ;
}
drop-down > ul > li {
  padding: 2px 5px ;
}
drop-down > ul > li:hover {
  background: lightgoldenrodyellow ;
}
<drop-down onchange="changeHandler()">
  <span>select value</span>
  <ul>
    <li>Chrome</li>
    <li>Firefox</li>
    <li>Opera</li>
    <li>Safari</li>
  </ul>
</drop-down>

<script>
  function changeHandler ()
  {
    console.log( 'changeHandler()', event.target.value )
  }
</script>