Without explaining the details of the project I'm working on in too much detail, I am trying to determine a way in which I can either implement an event listener for when document.getElementById().value is accessed, or override it in a similar way to an object, and define custom behaviour for the getting and setting of this value. Is this possible?
I've tried the following, to no avail:
Object.defineProperty(document.getElementById, 'nodeValue', {
get: function () {
console.log('document.getElementById value accessed');
return this.nodeValue;
},
set: function (value) {
console.log('document.getElementById value set');
this.nodeValue = value;
}
});
and the same as above but with value instead of nodeValue:
Object.defineProperty(document.getElementById, 'value', {
get: function () {
console.log('document.getElementById value accessed');
return this.value;
},
set: function (value) {
console.log('document.getElementById value set');
this.value = value;
}
});
Apologies if this seems a somewhat farfetched approach, the intricacies of JavaScript behind-the-scenes isn't something I am too familiar with. The above code does show what I am trying to achieve, however. I just don't know how!
I've spent some time on MDN trying to understand just how this works and from what I can gather, getElementById() returns an Element which inherits from the Node interface which contains a nodeValue, and I this that this nodeValue is what I am interested in, but I cannot be certain.
Edit: I'm looking for this behaviour to be generic, I have more than one (but an unknown number of) elements, so I'm not trying to apply this to a specific element.
First, modifying Element object is a bad idea. Extending it, is bad, but not as bad as modifying it. This is because, JavaScript implementation on web browser does not put details on how the underlying API works. For example,
1. Modify Element Object
We have a document as follows:
<div>
<h1 id="elem-id">Header Content</h1>
</div>
we could get its id by calling these instructions
let elem = document.getElementById('elem-id');
elem.getAttribute('id'); // 'elem-id';
We can modify getAttribute() by using:
Element.prototype.getAttribute = function (attributeKey) {
// Implement your own function here
return 'ok';
}
Next, when you call elem.getAttribute('id') it should return ok.
This is bad because there is no way to revert back to default implementation.
2. Extending Element Object
Next less bad stuff is, extending Object. We could simply do:
Element.prototype.getAttributeAndFireEvent = function (attributeKey) {
console.log(`getting attribute for ${attributeKey}`); // Could be an eventEmitter
return this.getAttribute(attributeKey);
}
then, we could use these methods by calling it like:
elem.getAttributeAndFireEvent('elem-id');
This will work but please be careful in modifying global object, it may cause unintended effect.
References:
In Javascript, can you extend the DOM?
Related
I am looking for a way to get the name of an object property like this:
let o = {
first: 1,
second: 2
};
function q(prop) {
// return name of prop
}
console.log(q(o.first));
// should return "first"
I am not even sure this is possible, but I am just asking.
The main use case for this is given by dynamic specification of requested attributes while keeping the advantages of using intellisense which can be quite significant for large projects and refactoring purpose.
I am not even sure this is possible...
Good instinct! It isn't. When you do:
q(o.first);
the value of the o.first property (1) is passed into q. Nothing about the object or its property is passed to q, just the value 1.
If you know what object the value came from, and if that object had only one property with a matching value, you could figure it out. But of course, one or both of those won't be true the vast majority of the time. In the general case, no, there's nothing to tell you that it came from o.first.
You try this with slight modification of your code
function q(obj,prop) {
if(obj.hasOwnProperty(prop))
return prop;
else
return null;
}
console.log(q(o,o.first));
Hope this help.
In Mozilla MDN
I have following example
var Animal = {
type: "Invertebrates", // Default value of properties
displayType : function() { // Method which will display type of Animal
return this.type;
}
}
So if I do
console.log(Animal.displayType()); \\Invertebrates
and
console.log(Animal.type); \\Invertebrates
Both the out put are same so what is the point of using a method to return the property of the function
Or what would be the right way to return an internal property of an object , Directly or using a function ?
Thanks & Regards
The point is that this function may compute what it returns, rather than read it from a property.
In general, functions like this, getters and setters, are said to enhance encapsulation and make your design more resilient against change. In addition to this, setters may trigger events, something that simple assignment to a property can't do.
By the way, the name 'property' in JS is very unfortunate, since in fact, in all other languages a property IS something only accessible via a getter and a setter.
Accessing attributes only via functions came in fashion when java beans were invented, to facilitate use of a tool called a bean box. Never mind what that is.
Applying getters and setters everywhere is wicked and totally unnecessary. It may easily slow your program down by factors.
They are very powerful, WHEN YOU NEED THEM, for the reasons I described. Don't only think at your current design, but also about future modifications. But adding getters and setters everywhere, in addition to making your code slow, will also make the amount of source code explode, which is in itself a hindrance to future modifications.
So you can use them if you deem it fit, but prefer common sense above all...
Sometimes you want to use its property to return some other information:
Example: You have a mapping of "Invertebrates" which returns the description of the word (dictionnary). Thus by calling displayDefinition() it will give you the definition
displayDefinition: function() {
return dictionnaryFind(this.type);
}
The difference is when you pass them around. If I pass Animal.type to a function, I get the type immediatly, but if I pass the function I can then run it later and get the type at a future point.
Imagine I have an AJAX call in my code, and in the success method I want to do something based on the type. If I pass the type into my function, it could potentially change before the success function is called. In this case, having a function is better because we can calculate the type at the time the success happens.
Consider the following code:-
function test(typeProp, typeFunc) {
$.ajax({
url: '/GetTest',
success: function() {
if(typeProp === 'Cat') {
//do something
}
if(typeFunc() === 'Cat') {
//do something
}
}
})
}
var Animal = {
type: "Cat", // Default value of properties
displayType : function() { // Method which will display type of Animal
return this.type;
}
test(animal.type, animal.displayType);
animal.type = 'Dog';
Once the code gets to the success function, typeProp will be 'Cat', but typeFunc will return 'Dog';
I'm using a function to create other functions that will be used on an document event handler so the signature of the returned functions must match that of the event handler, eg. function (event, ui).
The code is as follows
function createEventHandler(refFn, additionalMods) {
var createdEvent = function (event, ui) {
// Merge the properties of additionalMods with an object { event, ui }
// call function with refFn and the resulting object as parameters
}
createdEvent.ready = true;
return createdEvent;
}
I removed the code of the generated function for clarity but inside the refFn and additionalMods variables are processed inside.
Now when processing the user input I call the following line
var handler = events[i].handler.ready ?
events[i].handler :
createEventHandler(events[i].handler);
Basically process an array of data that each has a property called handler which is either a function or the result of calling createEventHandler.
The bottom line is that if the function in the array has been processed then pass this function 'as is', if not process the function and store the result so in the end all the functions are processed.
Right now i'm attaching a property called ready to signal that the function was processed as it turns out there is no reliable method to obtain the function's name according to this post but this doesn't feel right.
I also tried to use the prototype for comparison but this doesn't work because a new function is created everytime and is inside a scope so I can not get a reference for comparison.
I even tried
events[i].handler.prototype == createEventHandler().prototype
but of course it didn't work.
Does anyone know how can i generate this functions and have a reliable way to compare them to know if they were generated by my code or not.
{Edit}
To add further clarification
All the code above is under the same scope meaning the code that process the array has visibility over the createEventHandler function. I can modify this code all I want what I cannot modify is the content of the array once is created. I have to iterate over it as it comes and generate or not based on if the work was done already.
The createEventHandler is also exposed to the user throught an API function. Let's say the user calls evt.generate('model') this will generate an event handler that does an specific work using the createEventHandler function under the hoods. If then you can call evt.generate('bind') another will be generated that does another work.
This is a lot of behaviour that is provided to the users by default but they can choose to add they custom behaviour if none of the predefined ones are fit for the task.
All the data is declared once but the content of the array is disparate because I can write the following and is supposed to work. I omitted most of the other irrelevant properties.
var events = [
{
event: 'pageshow',
handler: evt.generate('model')
},
{
event: 'pagebeforeshow',
handler: function (params, last) {
// My custom handler for this case
}
}
];
After looping the array all the handlers are in the same format and ready to be binded. The createEventHandler is necessary in all the cases because I use dependency injection to supply data for those parameters so it's basically "if not called already then call it and do al the dependency injection work" this is why I need to compare the functions.
I found an elegant solution and I post it here in case someone runs into the same problem.
The problem with my code is that an user car write a function with a property named ready which is a common name and a value of true which is also a common value and the processing will fail.
Maybe the user didn't write the property, maybe is present because is inherited from it's prototype. The goal is to try to be as certain as possible that the code you are processing was the output or your own functions or not, which in javascript is almost an impossible task.
The most accurate way that I found was when I was reading about Equality comparisons and sameness which tells me that an object is only equal to itself when you use the === equality operator and is not a primitive object. That is
undefined === undefined => true
null === null => true
"foo" === "foo" => true
0 === 0 => true
But
{a:1} === {a:1} => false
So you can write a property ready which is equal to an object and as you hold the reference to that object all the comparissons will fail if this property was not set by you.
This is good but it feels bad to have an extra property called ready with a random object just to compare, maybe there is a better way and yes, there is.
In javascript there are no classes but there is prototype inheritance so you can write a function and use one of the patterns of inheritance to set this function as the ancestor of yours and use that for comparisons.
var parentEvent = function () {};
// This is the function that will be used as parent
function createEventHandler(refFn, additionalMods) {
var createdEvent = function (event, ui) {
// Merge the properties of additionalMods with an object { event, ui }
// call function with refFn and the resulting object as parameters
}
//createdEvent.ready = true This is no longer necessary
// This is the "sharing prototype" inheritance pattern
createdEvent.prototype = parentEvent.prototype
return createdEvent;
}
Now the prototype of your returned function is pointing to a function that you hold in a variable. Then you can compare them using
// Replace the property comparison with the prototype comparison
var handler = events[i].handler.prototype === parentEvent.prototype ?
events[i].handler :
createEventHandler(events[i].handler);
This is not fail proof, I know, but is good enough for most cases.
{Edit}
Thank's to #Bergi for pointing out that this is not inheritance in the strict sense of the word. The reason for that is that most javascript inheritance patterns demand that you use constructor functions and I'm using a factory function here. To make it work you have to write something like this
function createEventHandler(refFn, additionalMods) {
// Same code as before
createdEvent.prototype = parentEvent.prototype
return new createdEvent();
}
And the comparison is done with
events[i].handler.__proto__ === parentEvent.prototype
Note the difference in the way the function is returned ant the way the new prototype property is accessed. This is good when you do have other properties that you want to return that are in the parent function.
Do the contents of the events array change during the execution of your program, aside from replacing them with the converted versions?
If not, a simple solution is just to make a copy of the handlers before you start converting them, and use that for the comparison:
// keep this somewhere that you can access it later
var origHandlers = events.map(function (e) { return e.handler; });
var handler = events[i].handler === origHandlers[i] ?
createEventHandler(events[i].handler) :
events[i].handler;
I've read through most of the Javascript inheritance references, but I'm afraid I'm still scratching my head as to how to do this.
I'm trying to put together several classes, in the general sense, that will have similar behavior and thought using prototypes might be a way to achieve that. So I create a base class as follows:
function my_base_class()
{
var a_common_object = undefined; // The true value of this can't be set until runtime.
// some other stuff ...
};
Ideally, I'd like a_common_object to be private or at least protected, but just getting it working would be a good first step. I then need to create several derived classes of which this might be one:
function my_derived_class()
{
this.do_something_to_common_object = function()
{
// Here I need to reference my_base_class.a_common_object but
// at this point there's no relationship between the two classes
};
};
I now set the prototype of my_derived_class while creating an instance:
my_derived_class.prototype = new my_base_class();
var my_derived_class_inst = new my_derived_class();
So at this point I'm hoping that I have an object - my_derived_class_inst which has traits of my_base_class including the static object a_common_object which I can access.
I have two questions:
How do I refer to a_common_object within my_derived_class when
there's no relationship established between the two classes?
How can I then change a_common_object to its true value, so that
all derived classes seamlessly pick up the new value.
Please don't simply refer me to the standard reference web sites on inheritance as I've read most of them through and I'm still no wiser. It seems to me that the answer should be really simple but so far it escapes me. Many thanks.
do_something_to_common_object() really doesn't have a way of reaching a_common_object directly.
a_common_object isn't a member of the instance created for the prototype. It's a local variable scoped inside the constructor. So, only a function that's also defined within the constructor can reach it (ref: closures):
function my_base_class()
{
var a_common_object = undefined;
Object.defineProperty(this, 'a_common_object', {
get: function () {
return a_common_object;
}
});
// ...
}
function my_derived_class()
{
this.do_something_to_common_object = function()
{
console.log(this.a_common_object); // uses getter to retrieve the value
};
};
It would still be publicly accessible, but your options are limited as JavaScript doesn't yet support or have an equivalent to access modifiers.
Though, with Object.defineProperty(), it would at least be read-only so far and non-enumerable by default (won't appear in a for..in loop).
At least until #2, where you'd need to also have a setter. Though, it would be a chance to validate the value being storing it.
Object.defineProperty(this, 'a_common_object', {
// ....
set: function (value) {
if (/* validator */) {
a_common_object = value;
}
}
});
I have a potentially strange question about this and jQuery plugins
As I understand it, the following is a very basic jQuery plugin:
$.fn.clickclone = function(param){
return this.click(function(){
param.apply(this);
});
};
(pretending that's a plugin that somehow extends click().)
So, if I pass a function as an argument, it does what it needs to do and properly accesses this as a DOM node. Easy.
That's all clear to me.
What's not clear is, is there any way I could pass a non-function argument to the plugin and have it properly access this from the arguments? ie, could I configure the plugin to do something like this:
$("#foo").pluginname(["foo", $(this).text() ]);
Such that for:
Bar
It would properly pass an array to the plugin, with the second item in the array returning the value Bar?
I'm doing this, basically, to provide syntactic sugar for my plugin, where you can pass an array as a shortcut (in addition to using a normal callback function as the main functionality). Except, doing that, I lose access to use of this. Hence my dilemma.
EDIT: This is evil, but, it seems like one work around is to pass the argument as a string and then eval it. Not a workable solution for me, but, it illustrates what I'd like to be able to do:
$.fn.clickclone = function(param){
return this.click(function(){
if(typeof param === "function"){
param.apply(this);
}
else if(typeof param[1] === "string"){
console.dir("This is evil: " + eval(param[1]));
}
});
};
There's no general way to do this without a function, since, in the purely mathematical sense, you are asking for a function of the input (that is, a function of this): something that depends on this in a certain way.
You could perhaps hack it with strings, like so, but you lose the flexibility of functions:
$.fn.alertMe = function (methodToAlert) {
alert(this[methodToAlert]());
};
// usage:
$("#foo").alertMe("text");
$("#foo").alertMe("width");
And if you find using a function acceptable but the this syntax confusing, you can simply do the following:
$.fn.alertMe = function (alertGetter) {
alert(alertGetter($(this));
};
// usage:
$("#foo").alertMe(function (x$) { return x$.text(); });
$("#foo").alertMe(function (x$) { return x$.width(); });
And for completeness I guess I should mention you could probably get away with an eval-based solution, looking something like $("#foo").alertMe("$(this).text()"), but eval is evil and I will neither write up nor condone such a solution. EDIT: oh, I see you have done so in an edit to your original post. Good job corrupting future generations ;)