Custom methods and properties of html5 custom elements - javascript

My question is about the proper way of adding my own logic to custom elements.
I know how to create custom element, define a class extending HTMLElement, define callbacks like connectedCallback. It works.
The question is: how am I supposed to create my own methods and properties to support my custom logic? As I understand, defining them directly in my custom element class might cause conflict with current (or future) HTMLelement properties and methods.

1: Avoid any well known properties or functions unless you want to override them. If you are overriding them and you still want the old code to function make sure to call super in your functions, getters and setters.
2: Don't worry about future changes until they happen. Honestly there are not many changes to HTMLElement that will happen in each version of the language upgrade. Personally I just don't worry about it. I define whatever properties and functions I want. I often don't even worry about the existing functions.
For example I will use get title() and set title() and I won't bother calling super. Yes, I know I am breaking the existing model, but it doesn't matter for the component I did that to.
Please don't use the underscore '_' for public function names since the tradition is that those are supposed to be private functions and properties and should never be called by someone using the element.
Just write what you need to write. If someone using my component ever needed the original title functionality to work then I would fix it, but that will probably never be the case.

You can define them directly in the custom element class.
If you don't want them to cause confict with future properties and method, you can add a prefix like : underscore "_", "my".
class MyCustomElement extends HTMLElement {
constructor() {
super()
_init()
}
_init() {
this.attachShadow( {mode: 'open' } )
}
}
You could also create your own classes according to an design model. For example, if you use the MVC design pattern, you can create the class View, Model, Controller...

Related

Is it possible to access class properties without initialising the class?

I want to access all of the properties of a class that will be defined when the constructor is called, so that I can implement a sort of interface for the class.
Say I had a class that defines the property hello, I would like to access it to check it has been implemented and the type assigned to it is correct. Problem is, since all non-static class properties are tied to an instance, I can't get at them without instantiating the class, which I can't do.
In this situation, is it possible to access hello?
class MyClass {
constructor () {
this.hello = 'greetings';
}
}
In this situation, is it possible to access hello?
Not without using a JavaScript parser (like IDEs do to try to infer instance mbmers). hello, as you say, doesn't exist as a property until/unless an instance is created. With a parser, you can (usually) determine what the property names will be, perhaps sometimes their initial values, but that's all.

How to show an object provided as an argument by a user on a UML class diagram?

I have a JavaScript function, which is supposed to act like a class, and it needs several pieces of data from a user.
I decided to make it to expect one single object with those data pieces as its arguments, like:
new ClassFunction({
arg_1: 'foo',
arg_2: 'bar'
});
What is the best way to show this on a UML class diagram?
Should I write it as a comment in the curly braces or as a separate note or as an abstract class with a dependency line from the constructor class (ClassFunction) to that abstract class?
Or may be something else?
JavaScript function, which is supposed to act like a class
The more proper term would be object constructor; this may also give you a better idea of what you are actually trying to do.
Now the parameter provided to the constructor is some anonymous class that understands arg_1 and arg_2. UML models this via DataTypes, denoted with the <<dataType>> keyword.
A DataType is a kind of Classifier. DataType differs from Class in that instances of a DataType are identified only by their value. All instances of a DataType with the same value are considered to be equal instances. [UML Spec 10.2]
So for example your case could be modeled as the following:

How to deal with and discern inheritance conflicts in JavaScript?

So the code below reflects the pseudo-classical version of inheritance in JavaScript.
function SynthOne() { // constructor 1
this.sound1 = "piano";
};
function SynthTwo() { // constructor 2
this.sound2 = "bass";
}
SynthOne.prototype = new SynthTwo; // overwrite constructor 1's prototype with constructor 2's
var synthsCombined = new SynthOne; // assign constructor 1 to variable
// this variable now has access to both constructors properties & methods
document.write(synthsCombined.sound1 + " ")
document.write(synthsCombined.sound2)
But let's change this to make sound1 and sound2 to simply sound.
Let's also assume that I really wanted to create an inheritance chain to access both of these "sounds" even if they were named the same thing. Is there a pattern in the JavaScript community or a coding convention that exist to deal with this kind of situation? Or am I just stuck?
Thank you
Child properties hide properties of the same name further up the prototype chain. Technically, you can still get access to the parent property like this:
Object.getPrototypeOf(synthsCombined).sound
Or the non-standard:
synthsCombined.__proto__.sound
But this probably isn't something you want to do. If you need both values, then name them different things.
it was simply something that entered my mind and I was curious about. I can see a situation where at the very least you combine constructors not realizing they have similar property/method names.
You hardly inherit from classes whose set of properties1 you do not know. If you subclass something, you often want to explicitly overwrite properties with more specific values - that's just what the shadowing is for.
In case you want to extend the set, you'd have to choose an unallocated name. In case of interface clashes (e.g. when extending the implementation), that's just a bug and either the base class or the child classes would need to change their identifier. Using descriptive names will lower the risk.
How to deal with this kind of situation?
If it's unwanted, fix the bug (this is not JavaScript-specific). If you deliberately have chosen the same property name, you can access the inherited value by manually ascending the prototype chain with Object.getPrototypeOf().
[1]: Speaking of both attributes and methods, as they're just properties in javascript
You could give one of your constructors a base property, which will get the properties from the inherited constructor:
function SynthOne() { // constructor 1
this.base = {};
this.sound = "piano";
SynthTwo.call(this.base);
};
function SynthTwo() { // constructor 2
this.sound = "bass";
}
SynthOne.prototype = Object.create(SynthTwo.prototype);
var synthsCombined = new SynthOne;
console.log(synthsCombined.sound); //piano
console.log(synthsCombined.base.sound); //bass
But from what it looks like you are trying to accomplish, maybe inheritance is not the right way for you. It might make more sense to create a generic Synth class and maybe a SynthCollection class, to combine different Synths.

Extjs3.4 How to override the constructor

I have a need to override native constructor of one of Extjs classes. If to be more exact Ext.ensible.cal.DragZone, it is from extensible calendar.
What I have tried, and what solutions I have found
Put desired code to original code, but I want to avoid original code modifications. All my changes I want to keep in one place
Ext.override - it doesn't work. native constructor is called anyway
Fully duplicate the code from original library, but with my changes.
Ext.ensible.cal.DragZone = Ext.extend(Ext.dd.DragZone, {.....
});
But this causes some other unexpected error, seems that not all functionality is called(some of fields of the class, seems not to be properly initialized). But if I just put this code, instead of original, everything works fine(but see #1)
Create my own Ext.ensible.cal.DragZone by extending the original. Problem of this method, is that I need to review all the code of library, and override and/or extend all other classes which uses this DragZone, to make them to use mine.
So can you advise me smth. The most correct solution for me seems to be just constructor overriding.
How I overriding:
Ext.override(Ext.ensible.cal.DragZone, {
constructor : function () {
...
}
});
But as I told, original constructor is called
Here's a hack you can try, remember that the class name is the constructor, there is actually no constructor property on the class that is generated
Ext.ensible.cal.DragZone = function () {
// This is not very nice, it'd be better if you didn't have to
// assume what the parent is, but since we're already hacking it...
Ext.dd.DragZone.apply(this, arguments) ;
// Your code here
}
Note that this is not the safe approach as I mentioned in my comment, but should work
I suggest you use approach 4 so that your modifications only apply to classes that choose to use it.

How to add my own methods to HTMLElement object?

For example for this.parentNode I would like to just write this.p or instead of
document.getElementById('someid') just write document.g('someid'). Of course that are simple examples, I just want to know what is the correct way to do it.
(I know I can use jQuery or Prototype, but I'd like to learn how it is really done in JS)
Although you can prototype on the HTMLElement in many browsers - Internet Explorer (6,7,8) is NOT one of them. AFAIK, IE9 does support this (though I haven't tested it).
For browsers that do handle it, you can do:
HTMLElement.prototype.doHello = function(thing){
console.log(this + ' says: ' + thing)
}
document.body.doHello('hello')
I would strongly suggest not attempting to do this, for a few reasons:
Browser compatibility. While it is possible in several browsers, it isn't possible in IE <= 8.
DOM elements are host objects. Host objects (i.e. those provided by the environment that aren't native JavaScript objects) have no obligation to play by the same rules as native JavaScript objects and other than specified DOM behaviour can essentially do what they like. So, even if some browsers provide an HTMLElement prototype and allow you to augment it, there's no guarantee that it will work as you expect.
Compatibility with other code in your page. If any other code in your page (such as Prototype) messes with the HTMLElement prototype, you risk naming collisions and hard-to-detect bugs.
Instead, I would suggest creating wrapper objects around DOM nodes as jQuery, YUI and other libraries do.
Kangax has written a good article on DOM extensibility, covering all these points and more.
In a word, don't. It is best not to modify objects you don't own.
This is particularly true for HTMLElement, which you cannot modify in some browsers.
This article from perfectionkills.com will probably give you some insight into how it's done, and why you shouldn't do it.
(By the way, jQuery doesn't extend DOM elements. They use DOM wrappers instead.)
This might not be what you are looking for if you want to wrap a global object like document, but you can get a similar effect with custom-elements [1] [2] to create your own HTMLElement-like nodes.
create custom-element
add method to custom-element class
you can call the method
export class CustomElementInput extends HTMLElement {
log(){
alert("log")
}
// you can even overwrite methods like so
remove(){
alert("removing this node")
super.remove()
}
}
customElements.define("custom-element-input", CustomElementInput)
// somewhere else...
// in your HTML something like:
// <custom-element-input></custom-element-input>
const el = document.querySelector("custom-element-input")
el.log() // creates alert()

Categories

Resources