Search code examples
javascriptjquerydomcustomizationdomexception

Uncaught DOMException: Failed to execute 'define' on 'CustomElementRegistry': "[object HTMLElement]" is not a valid custom element name


Please help me I can't understand what the heck is causing the error. The code:-

class button extends HTMLElement{
    constructor(){
        super();
        const shadow = this.attachShadow({mode: 'open'});
        const button = document.createElement('button');
        button.style.cssText = 'display:block';
        button.textContent = super.textContent;
        shadow.appendChild(button);
    }
}
var Ω = (function () {

	'use strict';

	/**
	 * Create the constructor
	 * @param {String} selector The selector to use
	 */
	var Constructor = function (selector) {
		if (!selector) return;
		if (selector === 'document') {
			this.elems = [document];
		} else if (selector === 'window') {
			this.elems = [window];
		} else {
			this.elems = document.querySelectorAll(selector);
		}
	};

    /**
	 * Run a callback on each item
	 * @param  {Function} callback The callback function to run
	 */
	Constructor.prototype.each = function (callback) {
		if (!callback || typeof callback !== 'function') return;
		for (var i = 0; i < this.elems.length; i++) {
			callback(this.elems[i], i);
		}
		return this;
	};

	/**
	 * 
	 * @param  {String} className 
	 */
	Constructor.prototype.register = function (className) {
		this.each(function (item) {
			customElements.define(item, className);
		});
		return this;
	};
    /**
	 * Instantiate a new constructor
	 */
	var instantiate = function (selector) {
		return new Constructor(selector);
	};

	/**
	 * Return the constructor instantiation
	 */
	return instantiate;

})();
Ω('lol-foo').register(button);
<p>Expect a button down below</p>
<lol-foo>Hey</lol-foo>
It was thought that it will take the name from the first method(lol-foo here) and will add the class path and define it, but it seems that something wrong is written out and [object HTMLElement] is passed.

Your help will help custags.js help add a selector like jQuery($). Answers appreciated.


Solution

  • The issue is because you're providing a HTMLElement object to the first argument of define(), when it instead expects the custom element name as a string. For reference this name has to include a hyphen character, and be in lowercase. As such try this:

    customElements.define(item.tagName.toLowerCase(), className);
    

    Here's a working example:

    class button extends HTMLElement {
      constructor() {
        super();
        const shadow = this.attachShadow({
          mode: 'open'
        });
        const button = document.createElement('button');
        button.style.cssText = 'display:block';
        button.textContent = super.textContent;
        shadow.appendChild(button);
      }
    }
    var Ω = (function() {
      'use strict';
    
      /**
       * Create the constructor
       * @param {String} selector The selector to use
       */
      var Constructor = function(selector) {
        if (!selector) return;
        if (selector === 'document') {
          this.elems = [document];
        } else if (selector === 'window') {
          this.elems = [window];
        } else {
          this.elems = document.querySelectorAll(selector);
        }
      };
    
      /**
       * Run a callback on each item
       * @param  {Function} callback The callback function to run
       */
      Constructor.prototype.each = function(callback) {
        if (!callback || typeof callback !== 'function') return;
        for (var i = 0; i < this.elems.length; i++) {
          callback(this.elems[i], i);
        }
        return this;
      };
    
      /**
       * 
       * @param  {String} className 
       */
      Constructor.prototype.register = function(className) {
        this.each(function(item) {
          customElements.define(item.tagName.toLowerCase(), className);
        });
        return this;
      };
      /**
       * Instantiate a new constructor
       */
      var instantiate = function(selector) {
        return new Constructor(selector);
      };
    
      /**
       * Return the constructor instantiation
       */
      return instantiate;
    })();
    
    Ω('lol-foo').register(button);
    <p>Expect a button down below</p>
    <lol-foo>Hey</lol-foo>

    It's also worth noting that the second argument of define() is the class constructor, so the argument name of className is a misnomer. Its value is correct in this example, though.

    You can find more information on CustomElementRegistry.define() at MDN