I'm trying to use a constructor function to help create DOM elements but I'm wondering if there was a preferred way to do so. I know I could use a framework to do with this but I'd like to implement it using vanilla JavaScript.
Both ways shown below seem to work but I haven't used the new operator with functions very much. Is there any difference between the two ways? Would I be better just to use a plain old function in this situation and not use new?
// First way
function Title(text){
this.element = document.createElement('h2');
this.element.innerText = text;
return this.element;
}
// Second way
function Title(text){
var element = document.createElement('h2');
element.innerText = text;
return element;
}
var title = new Title("Hello");
document.body.appendChild(title);
Your first way does't seem to be correct. Though it works, you seems you haven't understood how new works in JavaScript. When you use new with a function, the following steps are taken:
An empty object is created, something like {}.
All this references inside the function refer to that empty object.
this is used to populate that empty object as needed.
implicitly this is returned. (If you explicitly return, this will be ignored.)
Note that in a constructor function, if you explicitly return something other than this, the returned value is not instanceof that constructor function. Only this is instanceof the constructor function.
Therefore, the first way has nothing to do with logic of new. It's logically the same as the second one.
Related
I came across this code for stripping Marketo forms of their included stylesheets. Let's assume that the code author is a super senior engineer. Array.from() could have been used instead of defining arrayFrom (functionally at any rate), so why use the latter?
For my part I'm trying to understand the arrayFrom definition (first line of the codeblock):
bind() sets this to the provided value, here [].slice (why?)
call() allows us to call getSelection with the this value bound by bind.
getSelection() returns a Selection object (or string in Firefox) of the selected text. This I'm unsure about.
In its use, arrayFrom gets passed an array (or NodeList) of stylesheets and returns an array of the same stylesheets (a shallow copy thereof?) no differently than if Array.from were used, so the functional bit of bind and call must be to alter the this value in a desirable way. Not sure how that acts on [].slice though.
Anyone? I'm clearly missing something.
const arrayFrom = getSelection.call.bind([].slice);
// remove element styles from <form> and children
const styledEls = arrayFrom(formEl.querySelectorAll("[style]")).concat(
formEl
);
styledEls.forEach(function (el) {
el.removeAttribute("style");
});
// create an array of all stylesheets in document
const styleSheets = arrayFrom(document.styleSheets);
// loop through stylesheets and check for ownerNode properties on each
styleSheets.forEach(function (ss) {
if (
//array of <link/> elements tied to stylesheets
[mktoForms2BaseStyle, mktoForms2ThemeStyle].indexOf(ss.ownerNode) !=
-1 ||
formEl.contains(ss.ownerNode)
) {
ss.disabled = true;
}
});
Nowadays we would just use Array.from. But your questions are about the construct that is used:
const arrayFrom = getSelection.call.bind([].slice);
First of all, this has nothing to do with getSelection, as the expression is not binding that, but the call function. This call function is on the Function prototype, so the above leads to the same result as:
const arrayFrom = Function.prototype.call.bind(Array.prototype.slice);
call is a function that allows one to call another function with the possibility to provide a this-argument to it. Here we define that the function to be called should be slice. The first argument we will provide to arrayFrom will be like the first argument we would provide to call, i.e. the object on which slice should be called. This gives it a similar behaviour as Array.from.
It may help to replace bind by this function that does a similar thing:
function arrayFrom(arrayLike) {
return Function.prototype.call.call(Array.prototype.slice, arrayLike);
}
It is confusing, but we invoke call with call so that we can provide a this argument to it (defining the function we want to call), making the second argument the this-argument that call (the first one) deals with.
I am trying to figure out how jQuery is both a function which accepts an argument and returns a value (selector -> array of elements), and also an object that can be extended with new methods:
$.fn.myMethod = function() {//do stuff}
As a company we are moving away from jQuery, because vanilla JS is so useable. However we have some jQuery plugins I wrote that we would like to keep using. I am re-writing them, but would like to keep the syntax for using them similar to how it was in JQ. I don't want to extend the Element object (element.myPlugin()) for obvious reasons, so I was thinking I would create my own wrapper like JQ. Ideally I could define the base/default function that returns an array of DOM elements like so:
const __ = function(element) {
if (typeof element === 'string') {
return document.querySelectorAll(element)
}
return [element]
}
But then later, this function could be extended with new methods:
__.myNewPlugin = function(text) {
this.forEach(el => el.innerText = text)
}
Where this is the array of DOM elements returned by the base/default function, so that later the rest of my team could use the new method like so:
__(document.querySelector('.thing')).myNewPlugin('Hi SO')
-or-
__('.thing').myNewPlugin('Hi SO')
If for some reason you think this is a bad idea, I'm happy to hear your reasoning, but please also post an example of how this is achieved. It's obviously possible (because JQ does it), so even if I decide not to go this route, I'd still like to learn how something like this could be implemented.
I use foreach for instances of class ( let say class1 ) inside prototype method of another class(class2), anyway it works well but when I try to use (this) to refer to some vales of class1 it doesn't work, any help?
Step 1: Look how to post questions on SO.
Step 2: If you program JS you need to know how this works, this case is not the real issue, the issue is you don't. Plenty of tutorials to google on that, I would recommend it since understanding how this works in JS is essential.
Depending on your actual code this could be enough to get you going:
The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.
So in a place where you call your function try using this beforehand:
var _this = this;
Then call your function:
whateverobj.myfunction().bind(_this)
I'm trying to create a jQuery style object class.
function obj(id){
if (this.__proto__.constructor !== obj) {
return new obj(id);
}
this.element = document.getElementById(id);
this.remove = function(){
this.element.parentNode.removeChild(this.element);
}
this.offset = function(){
return this.element.getBoundingClientRect();
}
}
obj(id).offset() // defined
obj(id).removeChild() //undefined
obj(id).appendChild() // undefined
obj(id).remove() // undefined
I got a problem. jQuery object can also use as a Javascript DOM object like $('#someid').innerHTML, but my object. I'm thinking about a solution that dynamically checks if a method does not exist in this object class, then return a DOM object return this.element.
How could I do this? Or any better ideas?
jQuery object can also use as a Javascript DOM object like $('#someid').innerHTML
No, it can't. innerHTML there would also be undefined. jQuery objects are wrappers around sets of elements. You could do $('#someid')[0].innerHTML (note the accessor, [0]).
...checks if a method does not exist in this object class...
If you want to test for the existence of a function, you can use typeof:
if (typeof obj.method === "function")
Note that for certain host-provided functions on some older browsers, you may get "object" instead of "function", so you have to allow for that.
But you'd have to do that where you're using the function.
I'm thinking about a solution that dynamically checks if a method does not exist in this object class, then return a DOM object return this.element.
This is a bad idea. Instead, provide a means of accessing the underlying element (the way jQuery does). It will be possible with ES6's proxies, but those aren't available widely yet.
How can one assign a Javascript namespace to an HTML element and call functions defined in said namespace on this element?
I asked this other question:
Attaching JavaScript to the prototype of an ASCX client side instance
The previous question answered the how to do it, but now I am curious how this works on a pure Javascript/HTML level. And I'm no closer to figuring it out.
Let assume I have an HTML page with just a textbox:
<html>
<body>
<div>
<input type="text" id="MyTextBox" />
</div>
</body>
</html>
In a browser, I can do document.getElementById('MyTextBox').
My question is however, using just javascript, how can I assign the object returned a javascript type so that from the object I can call functions defined in the namespace?
For instance, I want to do:
var x = document.getElementById('MyTextBox');
x.SetTheText('blah');
and in my custom namespace/type/class I would have defined SetTheText as
function SetTheText(text) {
this.value = text;
}
How do I say MyTextBox is an object that can run functions defined in a JS namespace.
I hope this makes sense
Basically, you can add any kind of property to (almost) any kind of JS object. And if you add a function to an object, that function's this will be the object.
In other words
var x = document.getElementById('MyTextBox'); // a DOM object
x.setTheText = function(text) {
this.value = text;
};
x.setTheText('blah'); // bingo
If you want to extend an entire class of objects, you can do that too, via the prototype
HTMLTextAreaElement.prototype.setTheText = function(text) {
this.value = text;
};
someRandomTextArea.setTheText("blah"); // bingo. Again.
However, this is not really recommended, as you're messing with objects that are outside your control (i.e. it's fragile as other scripts and browser updates and whatnot might get in the way). Also, you might break some other piece of code by doing this.
A better solution (in many ways) is the jQuery solution of wrapping an unmodified DOM element in another object, and calling methods on the wrapper object rather than the element directly (Personally, I rather like the "pretty" code that can come from just extending native JS objects, but alas, it's not safe, so I'm trying to quit that.)
p.s. Classes are Captilized in javascript; methods are camelCase. I've edited the code accordingly. It's not something that's enforced by anything, but style's style.
Edit: For comparison, you can look at the prototype.js library, which works its magic by extending the DOM. Again, in my opinion, it makes for some very pretty code compared to jQuery's constant invocations of $(...).xyz(...), but it's still a slightly dangerous route to take
You should not modify host objecs, all of the major libraries agree on that. Use a wrapper object instead:
<input type="text" id="inp0">
<script type="text/javascript">
function CreateCustomElement(el) {
this.element = el;
}
CreateCustomElement.prototype = {
setTheValue: function(text) {
this.element.value = text;
}
}
var x = new CreateCustomElement(document.getElementById('inp0'));
x.setTheValue('foo');
</script>