0

In javascript, if I have 2 strings myApp and my-app and was told that myApp is really a name of a class that extends HTMLElement, and my-app is that class's tagname, how can I use JavaScript to verify that (assuming that the class is already defined)?

Basically the test should pass if this was really defined already

class myApp extends HTMLElement {
    constructor() {
        super();
    }
}

customElements.define('my-app', myApp); 

Basically, it needs to check that myApp is a class and does extend HTMLElement (though it's possible it may extend something else, such as Polymer.Element, and on wards, but eventually, it must extend HTMLElement).

And then secondly, it must check my-app is the tag name of that class.

How can this be done in javascript?

I tried eval("myApp") but it did not work.

Supersharp
  • 29,002
  • 9
  • 92
  • 134
omega
  • 40,311
  • 81
  • 251
  • 474
  • for the first part, `myApp.prototype instanceof HTMLElement` should return true if myApp has HTMLElement in its ancestry – Jaromanda X Aug 06 '17 at 23:06
  • I'm not 100% sure if Polymer.Element extends HTMLElement, so I could probably do `myApp.prototype instanceof HTMLElement || myApp.prototype instanceof Polymer.Element`. But the other parts I have no clue. – omega Aug 06 '17 at 23:09
  • I tried `Polymer.Element instanceof HTMLElement` and that was false apparently. – omega Aug 06 '17 at 23:11
  • if myApp extends Polymer.Element extends HTMLElement, then HTMLElement is still an ancestor of myApp ... instanceof doesn't check one level of ancestry :p – Jaromanda X Aug 06 '17 at 23:11
  • `I tried` - just do what I suggested instead – Jaromanda X Aug 06 '17 at 23:11
  • I tried this `Polymer.Element instanceof HTMLElement` and it was `false`, so I have to check each individually, which is fine still – omega Aug 06 '17 at 23:12
  • I didn't say to try that, did I? it'd be `Polymer.Element.prototype` anyway - but that has nothing to do with the question – Jaromanda X Aug 06 '17 at 23:13
  • oh right, so polymer.element does extend htmlelement. – omega Aug 06 '17 at 23:13
  • no idea, it's your code :p – Jaromanda X Aug 06 '17 at 23:13
  • 2
    "downvote" description? If you are going to serial "downvote" you may as well leave your reason therefor as well. Since you evidently have a rational reason for your actions why keep the reason to yourself? – guest271314 Aug 07 '17 at 00:19

3 Answers3

4

You can use customElements.get() to get the custom element class from its tag name.

class myApp extends HTMLElement {
  constructor() {
    super()
  }
}
customElements.define( 'my-app', myApp )

console.log( customElements.get( 'my-app' ) === myApp )  //true
Supersharp
  • 29,002
  • 9
  • 92
  • 134
1

To check if the class exists and extends HTMLElement the check should be performed in the same scope as the class is declared by trying to declare myApp using const which should throw a SyntaxError or using console.assert().

Once first part is verified you can create the element and check the .is property or .tagName of the created element.

class myApp extends HTMLElement {
    constructor() {
        super();
    }
}

customElements.define('my-app', myApp); 

// string
let str = "myApp";
// get the object, if the object exists
const myApp_ = new Function(`return ${str}`)();
// check if `myApp` exists
console.assert(myApp_ === void 0
, myApp_
, [`${myApp_} is defined`
  , new RegExp(`${str} extends HTMLElement`).test(`${myApp_}`)]);
guest271314
  • 1
  • 15
  • 104
  • 177
  • The problem here is that you don't have a reference to the class name `myApp`. You just have the string "myApp" and "my-app". – omega Aug 06 '17 at 23:16
  • @omega That is not a "problem". It would be a problem if you did not have the strings. Pass the presumptive `class` identifier as string as the identifier to `console.assert()` to get a `Boolean` evaluation and pass the string as identifier as second parameter to get string representation of `class` – guest271314 Aug 06 '17 at 23:18
  • I'm not sure I follow, can you show an example? Your code above uses a variable name and not a string "myApp" and "my-app". – omega Aug 06 '17 at 23:19
  • @omega _"I'm not sure I follow, can you show an example?"_ See stacksnippets. _"Your code above uses a variable name and not a string "myApp" and "my-app"."_ Yes, once you have the string pass the string as an identifier to `console.assert()` – guest271314 Aug 06 '17 at 23:19
  • @omega The stacksnippets at the present Answer – guest271314 Aug 06 '17 at 23:21
  • sry, I'm still pretty lost lol. How do I pass the string into console.assert? – omega Aug 06 '17 at 23:22
  • @omega You do not pass the string to `console.assert()`. Once you have the string you pass the object reference having same characters as string to `console.assert()` – guest271314 Aug 06 '17 at 23:23
  • But the primary thing I don't know how to do is how to get the object reference from the string itself. – omega Aug 06 '17 at 23:24
  • Not sure what you mean? The string contains the exact same characters as the presumptive object. Pass the reference as an identifier to `console.assert()`. Alternatively `const myApp = void 0`, which should also throw an error `Uncaught SyntaxError: Identifier 'myApp' has already been declared` – guest271314 Aug 06 '17 at 23:25
  • I think you may have misunderstood the main question. The question is, how do I first get the reference to the class object, if I only have the string of it's name. Once I have the object, then I can probably check it's prototype to see if its instance of htmlelement, and then do .gettagname to check the last part. – omega Aug 06 '17 at 23:26
  • _"I only have the string of it's name"_ That is the object reference as a string. Pass the literal reference to `console.assert()` or try to declare again using `const`. `class` and `const` are not stored as properties of the global object, it is not possible to get expected result with `window[className]` – guest271314 Aug 06 '17 at 23:28
  • it's still confusing, are you saying I do console.assert("myApp")? if not, can you show with code please? Note, I don't have a literal reference in my code, I only have a string of the variable name literally. – omega Aug 06 '17 at 23:29
  • @omega The code is at stacksnippets. No, you do not pass the string, you pass the object reference that you get from the string `console.assert(myApp === void 0, myApp, ["myApp is defined"])`, else it is not possible to query for unknown `const` or `let` or `class` variables at the global object using bracket notation as the declarations are not set as properties of the global object. Also, the same scope is necessary. The same `const` or `class` identifiers can be declared again within `try..catch`` within error being thrown – guest271314 Aug 06 '17 at 23:31
  • I saw that code, but what you are doing is assuming you already have the object reference, since you are putting that in console.assert(). But when you say `object reference that you get from the string`. I have no idea what you mean by that. How can you just get that from a string, Do you mean with `eval()`? – omega Aug 06 '17 at 23:33
  • what is the regular expression check for? – omega Aug 07 '17 at 00:14
  • To check for `"myApp extends HTMLElement"` at `myApp.toString()`, which template literal `${myApp_}` ultimate returns – guest271314 Aug 07 '17 at 00:15
  • isn't it enough to do `myApp_.prototype instanceof HTMLElement` ? – omega Aug 07 '17 at 00:16
  • @omega Yes. There are various methods of determining the result – guest271314 Aug 07 '17 at 00:17
1

Ok I figured it out, I can just do

    var worked = false;
    var claimedClassName = 'myApp';
    var claimedElementName = 'my-app'; 

    try {
        const r = new Function(`return ${claimedClassName}`)();
        if (!(r.prototype instanceof HTMLElement)) {
            throw new Error('Class is not instance of HTMLElement');
        }
        var a = new r();
        if (a.tagName.toLowerCase() != claimedElementName.toLowerCase()) {
            throw new Error('Tagname does not match');
        }
        worked = true;
    } catch (err) {
        alert(err.message + "\n" + err.stack);
    }

    if (worked) alert("pass!")
    else alert("fail!");
omega
  • 40,311
  • 81
  • 251
  • 474
  • If your Answer resolves Question you can accept your own Answer https://stackoverflow.com/help/self-answer – guest271314 Aug 07 '17 at 00:05
  • Your function version is probably better. I will accept yours instead. – omega Aug 07 '17 at 00:09
  • 2
    I implemented custom elements in Chrome and I think your code has a problem. IIRC tag names are ASCII-lowercased, but you use toLowerCase which I think also lowercases non-ASCII. customElements.get is simple and direct--use that. – Dominic Cooney Aug 08 '17 at 14:23