I am wanting to attach into an Elements default property such as innerHTML as a backup under an object that way it does not pollute the Elements properties. so to help give an idea of what I am trying to achieve and what currently works:
Element.prototype._backupPropertyDescriptors = {};
Element.prototype._backupProperties = {};
Element.prototype._backupPropertyDescriptors._innerHTML = Object.getOwnPropertyDescriptor(Element.prototype,'innerHTML');
//This is what I want to do but loses Elements scope:
Object.defineProperty(Element.prototype._backupProperties,'_innerHTML',Element.prototype._backupPropertyDescriptors._innerHTML);
//the scope has changed from element to _backupProperties so this property fails.
//The working version:
Object.defineProperty(Element.prototype,'_innerHTML',Element.prototype._backupPropertyDescriptors._innerHTML);
//the reason for this is I want to be able to manipulate the get and set such as:
Object.defineProperty(Element.prototype._backupProperties,'_innerHTML',{configurable:true,enumerable:true,get:function(){console.log('getting',this.innerHTML);return this.innerHTML},set:function(val){console.log('doing something here before setting');this.innerHTML = val;}});
The problem with this is once it is inside of backup the this statement no longer holds the element...
I know one way to do this would be to use a bind or call but that still poses the how do I get the elements scope... as this during define property is the window..
So for anyone looking to try and do this, here is the solution I found :) might be something better out there, but this does work. requires only 3 properties in the prototype and then all others get put inside a single one.
Element.prototype._backupPropertyDescriptors = {};
Element.prototype._backupProperties = {};
Object.defineProperty(Element.prototype,'_backupProvider',
{
writeable:false,
enumerable:true,
configurable:true,
get:function()
{
var _backupProperties = this._backupProperties;
_backupProperties._Element = this;
return {_Element:this,_backupPropertyDescriptors:this._backupPropertyDescriptors,_backupProperties:_backupProperties};
}
});
//These first ones set up the main provider and property and descriptor holders.
//then just copy a descriptor:
Element.prototype._backupPropertyDescriptors._innerHTML = Object.getOwnPropertyDescriptor(Element.prototype,'innerHTML');
//and assign it to a new property inside the backupProperties:
Object.defineProperty(Element.prototype._backupProvider._backupProperties,'_innerHTML',
{
enumerable:true,
configurable:true,
get:function()
{
return this._Element._backupProvider._backupPropertyDescriptors._innerHTML.get.call(this._Element);
},
set:function(val)
{
console.log('setting html to: ',val);
this._Element._backupProvider._backupPropertyDescriptors._innerHTML.set.call(this._Element,val);
}
});
//and if you wanted to do something really crazy.... like overwrite the original..
Object.defineProperty(Element.prototype,'innerHTML',
{
enumerable:true,
configurable:true,
get:function()
{
return this._backupProvider._backupProperties._innerHTML;
},
set:function(val)
{
console.log('setting html to: ',val);
//do some crazy two way template binding here or something else crazy
this._backupProvider._backupProperties._innerHTML = val;
}
});
that is all.. thanks for the help #Bergi
Related
I am very new to javascript.
Here I am failing to run an object method on a DOM element that I selected through another property of the same object. I suspect there is something wrong with my thinking!
Thanks in advance for any piece of help.
var Arrow = function() {
this.current = $('.arrow');
this.previous = null;
this.bend = function() {
// do bend
};
};
var arrow = new Arrow();
arrow.current.bend();
bend() is a method of Arrow, not current. Use arrow.bend() and it will also have access to current using this.current.
arrow.current.bend is not defined.
You have defined:
this.current as the Array of DOM elements.
this.bend as method with a function.
Hence, you can call:
arrow.current >> returns Array of DOMs
arrow.bend() >> executes function bend.
arrow.current.bend() does not exist.
Also, note that arrow.current is an array. You'd first need to get each of the elements:
for (element of arrow.current) { element.bend(); }
However, as said before, element does not have a bend element by default and you have not appended at any point. Only arrow has a bend property.
I hope this guides you on why this does not work.
However, if you want to open a question on what you are trying to achieve, maybe we can help to get it fixed.
You need to call bend() on arrow object. In bend() function, you do what you need to do.
var Arrow = function() {
this.current = $('.arrow');
this.previous = null;
this.bend = function() {
// do bend
current.style = 'bent';
};
};
var arrow = new Arrow();
arrow.bend();
So two things.
You called the right method on the wrong object
arrow.bend(); // not arrow.current.bend()
The second possible problem is with this.current = $('.arrow');. To get the an element from the DOM, you should make sure it's totally loaded. I'd suggest the following
var Arrow = function($arrow) {
this.current = $arrow;
this.previous = null;
};
// To avoid creating the `bend` in every instance of Arrow
Arrow.prototype.bend = function() {
console.log(this.current.attr('id'));
};
$(function () {
// Now it's certain that the DOM is completely loaded
var arrow = new Arrow($('.arrow').first());
arrow.bend();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="arrow" id="toto">arrow<div>
I want to add a variable or a function to my mdDialog. Im not to sure on how to create a custom mdDialog, Im new to angularjs.
This is my mdDialog:
vm.dialog_up = function() {
vm.dis = true;
alert = $mdDialog.alert()
.title('Attention, ')
.content('Do you want to edit your Information?')
.ok('Close');
$mdDialog
.show( alert )
.finally(function() {
alert = undefined;
});
}
I want to maybe add a function to the .ok button.
JavaScript is a very liberal language and it allows you to add properties and methods to objects. For example:
var modal = {};
modal.x = 5;//this assigns the value of `5` to the newly attached property `x`
modal.testMethod = function() {
//Do something here
}
PS:
Though personally, I think that modifying framework objects can cause side effects.
I'm creating a custom combobox which uses jQuery validator.
At first they all are gray except the first (it means Country). When I choose 'Slovenská republika' the second combobox is enabled.
They all are instances of a a custom autocomplete combobox widget.
To enable the validation I use this code (which is called within _create: function(){..})
There you can find $.validator.addClassRules(); and $.validator.addMethod(). I also added the appropriate class so it really does something.
_registerCustomValidator: function(){
var uniqueName = this._getUniqueInstanceNameFromThisID(this.id);
var that = this;
console.log(this.id);//this prints 5 unique ids when the page is being loaded
$.validator.addMethod(uniqueName, function(value,element){
if(!that.options.allowOtherValue){
return that.valid;
}
console.log(that.id);//this always prints the ID of the last combobox StreetName
return true;
}, "Error message.");
var o = JSON.parse('{"'+uniqueName+'":"true"}');
$.validator.addClassRules("select-validator", o);
}
//this.id is my own property that I set in _create
Problem: When I change the value of any instance of combobox, it always prints the ID of the last instance StreetName, but it should belong to the one that has been changed.
I thought it might be because of registering $.validator.addMethod("someName",handler) using such a fixed string, so now I pass a uniqueName, but the problem remains.
Therefore the validation of all instances is based on the property allowOtherValue of the last instance.
I don't understand why it behaves so. Does anyone see what might be the problem?
EDIT:
see my comments in the following code
_registerCustomValidator is a custom function within a widget factory.
//somewhere a global var
var InstanceRegistry = [undefined];
//inside a widget factory
_registerCustomValidator: function(){
var i=0;
while(InstanceRegistry[i] !== undefined) ++i;
InstanceRegistry[i] = this.id;
InstanceRegistry[i+1] = undefined;
var ID = i; //here ID,i,InstanceRegistry are correct
$.validator.addMethod(uniqueName, function(value,element){
//here InstanceRegistry contains different values at different positions, so its correct
console.log("ID=="+ID);//ID is always 5 like keeping only the last assiged value.
var that = InstanceRegistry[ID];
if(!that.options.allowOtherValue){
return that.valid;
}
return true;
}, "Error message");
var o = JSON.parse('{"'+uniqueName+'":"true"}');
$.validator.addClassRules("select-validator", o);
}
It looks like a sneaky combination of closure logic and reference logic. The callback in $.validator.addMethod is enclosing a reference to this which will equal the last value of this when $.validator.addMethod. (Or something like that?)
Glancing at your code, it's not clear to me what this is in this context. So I can't really offer a concrete solution. But one solution might be to create some kind of global registry for your thises. Then you could do something along the lines of:
_registerCustomValidator: function(){
var uniqueName = this._getUniqueInstanceNameFromThisID(this.id);
$.validator.addMethod(uniqueName, function(value,element) {
var instance = InstanceRegistry[uniqueName];
if(! instance.options.allowOtherValue){
return instance.valid;
}
return true;
}, "Error message.");
var o = JSON.parse('{"'+uniqueName+'":"true"}');
$.validator.addClassRules("select-validator", o);
}
The registry could be keyed to uniqueName or id, just so long as it is a value getting enclosed in your callback.
Given a javascript object like this:
var myThing = {};
Object.defineProperty(myThing, 'gen', {
'get' : function() {
// access caller name here, so I can return cool/neat stuff
}
});
I want to be able to get children of myThing.gen, but know what is being asked for in the getter.
for example:
var coolThing = myThing.gen.oh.cool;
var neatThing = myThing.gen.oh.neat;
I want the "oh.cool" or "oh.neat" part in getter, so I can make decisions based on this, and return something specific to it. I am ok with solution not working in IE, or old browsers, as it is primarily for node.
The actual purpose of this is so that I can request myThing.gen.oh.neat and have the myThing.gen getter resolve to require('./oh/neat.js') and return it.
Since require cache's, this is an efficient way to dynamically load modular functionality, and have a tidy interface (rather than just dynamically building the require where needed) without having to know the structure ahead of time.
If there is no introspection-of-name function that can get this for me, I could just do something less elegant, like this:
myThing.gen = function(name){
return require('./' + name.replace('.', '/') + '.js');
}
and do this:
neatThing = myThing.gen('oh.neat');
I don't like this syntax as much, though. I looked at chai's dynamic expect(var).to.not.be.empty stuff, but couldn't figure out how to do it completely dynamically. Maybe there is not a way.
without actually solving the problem of dynamically discovering the caller, I can do this:
var myThing = {};
Object.defineProperty(myThing, 'gen', {
'get' : function() {
return {
'oh':{
'cool': require('./oh/cool.js'),
'neat': require('./oh/neat.js')
}
};
}
});
Is there a way to do this dynamically?
You can't see what the property gen will be used for in the future, so you would need to return an object with properties that react to what the object is used for when it actually happens:
var myThing = {};
Object.defineProperty(myThing, 'gen', {
'get' : function() {
var no = {};
Object.defineProperty(no, 'cool', {
get: function(){ alert('cool'); }
});
Object.defineProperty(no, 'neat', {
get: function(){ alert('neat'); }
});
return { oh: no };
}
});
Demo: http://jsfiddle.net/UjpGZ/1/
I created a function to return me a jquery element.
function GetDialogButton() {
return $('a.dialog');
};
This was done as the same element was used within multiple other functions. I thought it best if it was obtained from a single place, therefore making it easier to change in future should the atribute name change.
I would like to improve this getter so that it does not perform a search everytime when called multiple times within a single page load.
How can I do this? do I cache it? or perhaps there is no need as this is optimised out?
You can create a cache variable, but it will pollute the global namespace again
var dialogButton;
function GetDialogButton() {
if(dialogButton){
return dialogButton;
}
dialogButton = $('a.dialog');
return dialogButton;
};
Creating a global cache variable is not necessary. You can do it without adding a variable to the global scope. Something like this would do:
var GetDialogButton = (function() {
var set;
return function() {
if (set === undefined) {
set = $('a.dialog');
}
return set;
};
}());
Well, you could lazy-load it.
var $dialogButton = null;
function GetDialogButton() {
if($dialogButton == null)
$dialogButton = $('a.dialog');
return $dialogButton
};
Another alternative, if you expect there to only be one dialog button you could give the element an id and then the act of searching for it will be more efficient
<a id="dialogButton">...</a>
$('#dialogButton')... // nice and quick
You could keep the global namespace clean by;
function GetDialogButton() {
if (typeof GetDialogButton.element === 'undefined' ) {
GetDialogButton.element = $("a.dialog");
}
return GetDialogButton.element;
};