nepdev nepdev - 5 months ago 19x
HTML Question

Custom HTML element attribute not showing - Web-Components

I'm exploring web-components custom HTML elements, and I am running into a problem adding custom attributes to my custom elements: any value I set in the markup is never honored.
For a simple element like this, which should show the text supplied in the "flagtext" attribute, it does always show the default value.

<test-flag flagtext="my text"></test-flag>

Full JSBin sample is here.

The JSBin uses the Polymer library (as this is the only thing I can pull in there). I am using webcomponents.js generally, same result. Both in Chrome 49 and in Firefox 45 gives same result. There is no error showing in the console or debugger.

Every sample I find on the web has similar code but I tried various versions and it always refuses to update. I even copied a few samples into JSBin and they do not work either.

What could be wrong? I understand it is experimental technology but the consistency with which this isn't working is still surprising. Has this standard been abandoned? (I see that the latest April 2016 W3C draft of custom elements has entirely changed its approach.)

When I define "attributeChangedCallback" function, it does not fire.
I can easily modify the property via Javascript, this is not a problem.

But why can I not specify the property in markup, as I am supposed to?

Edit - full code

Note that you'll need to put these into separate files for the HTML import, and that you need the "webcomponents-lite.js" library.

Main HTML file

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
border: 1px solid blue;
<script src="lib/webcomponents-lite.min.js"></script>
<link rel="import" href="test-flag.html">

Here is the custom test-flag component:
<test-flag flagtext="my text"></test-flag>

File: test-flag.html



<div id="testflagID">



(function() {

var _currentScript = (document._currentScript || document.currentScript);
var importDoc = _currentScript.ownerDocument;
var customPrototype = Object.create(HTMLElement.prototype);
Object.defineProperty(customPrototype, 'flagtext', {value: '(default)', writable: true});
document.registerElement('test-flag', {prototype: customPrototype});

customPrototype.createdCallback = function()
var template = importDoc.querySelector('template');
var clone = document.importNode(template.content, true);
var idx = clone.querySelector("#testflagID");
idx.textContent = this.flagtext;
var root = this;
var createdElement = root.appendChild(clone);





There are 2 concepts that are not automatically binded.

In the HTML code:

<test-flag flagtext="my text"></test-flag>

...term flagtext is an HTML attribute (not a property).

In the JavaScript code:

Object.defineProperty(customPrototype, 'flagtext', {value: '(default)', writable: true})

...term flagtext is a JavaScript property (not an attribute).

For standard elements, the browser automatically binds the property value to the attribute value (and vice versa). For Custom Elements, too (with standard attributes). But if you want to add a custom attribute, you'll have to bind it manually.

For example, in the createdCallback() method, add:

this.flagtext = this.getAttribute( 'flagtext' ) || '(default)'

Live sample:

document.registerElement( 'test-flag' , {
  prototype: Object.create( HTMLElement.prototype, {
    flagtext: {
      value: '(default)',
      writable: true
    createdCallback: {
      value: function() 
        this.flagtext = this.getAttribute( 'flagtext' ) || this.flagtext
        this.innerHTML = 'flagtext=' + this.flagtext
  } )
} )
<test-flag flagtext='new content'>Hello</test-flag>

NB: the attributeChangedCallback() method is fired only when an attribute is changed after element creation (which is not the case here).