How to access custom element from within a linked javascript file? - javascript

If I had a script like this
<template id="x-foo-from-template">
<script src="/js/main.js"></script>
</template>
<script>
customElements.define('my-header', class extends HTMLElement {
constructor() {
super();
let shadowRoot = this.attachShadow({mode: 'open'});
const t = document.currentScript.ownerDocument.querySelector('#x-foo-from-template');
const instance = t.content.cloneNode(true);
shadowRoot.appendChild(instance);
// set up title
var title = this.getAttribute("title");
var div = document.createElement("div");
div.innerText = title;
shadowRoot.appendChild(div);
}
});
</script>
From within main.js how can I access the custom element which is equivalent to this in the constructor()?
Thanks

You cannot do that as explained in this thread: The currentScript property will return null.
Instead you should load the script outside of the <template>, and invoke a function defined in the script from your custom element callbacks connectedCallback() or constructor().

You must access currentScript outside of the component. I use this:
var frag = (document.currentScript||document._currentScript).ownerDocument.querySelector('template').content;
I use (document.currentScript||document._currentScript) to handle polyfills.
Then in your constructor you would use:
const instance = frag.cloneNode(true);
At this point frag is a document-fragment and can contain any number of children. All of these will be replicated by the call to cloneNode so that each instance of your Web Component has its own copy of DOM.

Related

Embedding a custom HTMLElement into another custom HTMLElement

I'm wondering what best practices are for creating an object that extends HTMLElement and embedding it within another object that extends HTMLElement. I'm interested in an implementation that creates these objects and adds them to the DOM programmatically.
Use case: I can create e.g. a Menu component and re-use it throughout my app.
The below code works but I think it can be optimized.
Edit: It was pointed out (and I agree) that there isn’t really a question here.
Here’s a question: I only want to call customElements.define() once per element. What is the scope of this call? I tried to call it in the TodoMenu constructor and then use that element within Todo to no avail.
class Todo extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({mode: 'open'});
const wrapper = document.createElement('div');
customElements.define("todo-menu", TodoMenu);
const todoMenu = document.createElement("todo-menu");
wrapper.appendChild(todoMenu);
shadow.appendChild(wrapper);
}
}
class TodoMenu extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({mode: 'open'});
const wrapper = document.createElement('div');
// Some UI stuff
shadow.appendChild(wrapper);
}
}
customElements.define("todo", Todo);
const todoItem = document.createElement(Todo);
document.getElementById('test-todo2').appendChild(todoItem);

I want to write a class whose instances are functions

In typescript, how would I successfully go about creating a class whose instances are functions? Specifically, I want all the functions in the class to return an HTMLelement.
I want to be able to have functions in that class that look like:
function givDiv() {
const div = document.createElement('div')
div.id = 'givenDiv'
// other things I might want to do to div
return div
}
Yes, this is possible. You can use the class constructor to return the element that you want. E.g.:
class MyDiv {
constructor() {
return document.createElement('div');
}
}
Usage:
const div = new MyDiv();
/* returns a <div></div> */
I dont know your scenery, but this can be very counterintuitive. You may consider using static methods. E.g.:
class CreateElement {
static create(el, props) {
return Object.assign(document.createElement(el), props);
}
static div(props = {}) {
return CreateElement.create('div', props);
}
static p(props = {}) {
return CreateElement.create('p', props);
}
}
On this class we have the generic method "create" which creates any element with the properties we assigned in "props". We use this method to create specialized methods that creates our elements for us like this:
const div = CreateElement.div();
/* creates a <div></div> */
const p = CreateElement.p({ classList: 'nice' });
/* creates a <p class="nice"></p> */
It sounds like you mean you want to create a class whose methods return HTML elements (methods are already functions):
class MyClass {
givDiv() {
const div = document.createElement('div')
div.id = 'givenDiv'
// other things I might want to do to div
return div
}
}
const myInstance = new MyClass()
// `myInstance` is an instance of `MyClass`
const div = myInstance.givDiv()
// call the `givDiv` method on the instance, returning an HTMLDivElement

How to define a more customElements for same Class

I jsut tried to define new Custom Elements.
// the class
class MyEl extends HTMLElement {
constructor() {
// mandatory super call to upgrade the current context
super();
// any other Class/DOM related procedure
this.setAttribute('custom-attribute', 'value');
this.innerText = 'new element';
}
}
// its mandatory definition
customElements.define('my-el', MyEl);
// the JS way to create a custom element
const newMe = new MyEl();
document.body.appendChild(newMe);
Defining a new element of the same class will cause an error:
"Uncaught DOMException: Failed to execute 'define' on 'CustomElementRegistry': this constructor has already been used with this registry"
But i want to define new elements for same class.
Could you suggest a way to display multiple items from the same class (MyEl).
Thank in advance!
Just try with an anonymous constructor and extend your MyEl:
class MyEl extends HTMLElement {
// your definitions
}
customElements.define("my-better-element", class extends MyEl { })
customElements.define("my-second-element", class extends MyEl { })
customElements.define('my-el', MyEl) must be called only once to tell the browser you have a new <my-el> tag (aka to define a custom element).
Then you can create multiple elements just by using new:
const newMe1 = new MyEl();
const newMe2 = new MyEl();
Or createElement():
const newMe1 = document.createElement( 'my-el' )
const newMe2 = document.createElement( 'my-el' )
Or, in the html code:
<my-el></my-el>
<my-el></my-el>

Javascript inheritance using node

I'm want to use my Parent's class methods in my child class.
In classical OOP, you would simply extend your child class to make use of your parents' functionality, is this possible using prototype?
Here is my file structure:
Parent.js
var Parent = function(){
this.add = function(num) {
return num + 1;
};
};
module.exports = Parent;
Child.js
var Parent = require("./parent.js"),
util = require("util");
var Child = function() {
this.sum = function(num) {
// I want to be able to use Parent.add() without instantiating inside the class
// like this:
console.log(add(num));
};
};
util.inherits(Child, Parent);
module.exports = Child;
program.js
var child = require("./child.js");
var Calculator = new child();
Calculator.sum(1);
Obviously, add() is undefined here.
I've tried using util.inherits but it I'm not sure it's the right approach.
I'd also like to ask if this is a good design pattern in JavaScript in general, considering I'd like to have multiple child classes inheriting from my parent?
There are two issues with your code:
First, as mentioned in the comments by #Pointy, the add method in Child.js should be qualified with this.. This is because using add will resolve it to the root scope (window in browsers).
Second, you are binding the add method in Parent to each particular instance independently, by using this.add = function(...){...}. Bind it to the Parent prototype and you'll get what you want.
var Parent = function() {}
Parent.prototype.add = function(num) { return num + 1; }
The function Parent.prototype.add will be inferred to all instances of Parent and its derived objects.

Use a class in another class

In my project, I kept all common methods in a file and named its class as SandBox. I am loading this file in <head>.
Now I need to use this SandBox class in another class which is page specific. The class name is Home class. I am loading this Home class just above </body>.
How should I use SandBox class in Home class?
I tried the below in my Home class where I can access methods of SandBox class only in initialize property but I need to access it in all methods of Home class without repeatedly declaring as new SandBox() in each method.
var PdClicksClass = Class.create();
PdClicksClass.prototype = {
initialize : function(){
var sb = new SandBox();
sb.attachEvents(/* some arguments here */);
},
toggleReviewSection : function(e){
//SandBox class methods should also use here
},
openImagePopup: function(e){
//SandBox class methods should also use here
}
};
var projectDetails = new PdClicksClass();
PS: I don't think declaring new SandBox() in each method of Home class (as I did in initialize above) is efficient one though.
Here's an example of an object property way I would do this
function PdClicksClass() {
/* other class stuff here */
this.sb = new Sandbox();
}
PdClicksClass.prototype = {
getSandbox : function() {
return this.sb;
}
}
Or if encapsulation isn't an issue, simply call it by PdClicksClass.sb
Instead of
var sb = //etc
You want to do
this._sb = //etc
Then you can refer to
this._sb
The underscore is often used in js as convention for private values.

Categories