dynamically check if function doesn't exist in object class - javascript

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.

Related

Constructor function to create DOM elements

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.

Inheriting from the DOM Element object in javascript ! Whats wrong with this code?

I am trying to inherit from the DOM Element object,all code runs fine when I create the object but when I try to call the appendChild() method ,it gives an error saying :
MyObject doesn't have an appendChild method
Here is my code:
var content=document.createTextNode("This was dynamically created");
function MyObject(tagName){
Element.constructor.call(this,tagName);
this.prototype=Element.prototype;
this.prototype=Object.defineProperties(this.prototype,{
newMethod:function(){
//dosomething
}
});
}
var newObj=new MyObject("div");
newObj.appendChild(content);
Though you're doing it incorrectly (more on that later), you're ultimately trying to pass an object that inherits from a DOM Element instead of a DOM Element itself. This is not allowed.
It seems like it should work, but DOM Elements and DOM methods are host objects. They don't play by all the same rules that you'd expect from native objects. The .appendChild() method wants an Element, and nothing else. So what you're trying to do won't work.
With respect to your approach to inheritance, it's entirely incorrect. You don't modify the .prototype property of the new object being created. You modify the .prototype of the constructor function. It's done once, and then all new objects created from the constructor inherit from the object assigned to that constructor's .prototype property.
Because there's no inheritance the way you have it, there's no .appendChild() method. The code below fixes it, but it'll still not work because of the reason given above.
function MyObject(tagName){
Element.call(this, tagName);
}
MyObject.prototype=Object.create(Element.prototype);
Object.defineProperties(MyObject.prototype,{
newMethod: {
value:function(){
//dosomething
}
}
});
Your property descriptor syntax was also wrong, so I fixed it.
This is because calling the constructor function of the Element doesn't create an instance of the DOM Element object rather it tries to set the attributes which are set by the constructor function of the DOM Element object,For example
function MyObject(tagName){
Element.constructor.call(this, tagName);
}
var newObj=new MyObject("div");
will not create a tagName attribute like the one available when we create an instance of the DOM Element object and
alert("Tag Name is set to "+newObj.tagName);
will display
Tag Name is set to undefined
as the constructor function tried to set it but it could not because there was no tagName attribute but If I replace Element.constructor.call(this, tagName); with document.createElement(tagName) you will get the result
Tag Name is set to DIV

What does $('<div>').parent() return?

I'm working on a jQuery widget that attaches events to the widget's parent, but I'm unable to tell if it has a parent.
For example;
var x = $('<div>');
x.mywidget();
........... in mywidget
_create : function () {
var y = this.element.parent() === undefined ? this.element : this.element.parent();
y.bind(....);
}
I need to check if the widget has been added to the DOM before I do the bind statement. If it has not been added to the DOM, then I'll just bind this.element.bind(....) instead.
The problem is that $('<div>').parent() returns a jQuery object! I was expecting that it would return undefined.
So I'm wondering what parent could it be returning when it shouldn't have a parent?
You may use myDiv.parent().length to know if the jQuery set is empty or not.
But this will yield false positives if the object wasn't removed from the DOM directly but it parent was.
If you want a reliable detection, then, you should use jQuery.contains(document.documentElement, myDiv).
It will always return an object. If you want to see whether anythings in the object, you can check for .length == 0, so $("<div>").parent().length == 0 would be your check.
Check the length of the jQuery object returned. If your div has no parent, the jQuery object returned by .parent will wrap zero elements.
All jQuery DOM searching and manipulation methods return a jQuery collection with 0 or more elements. $("<div>").parent() returns a collection with no elements (an empty collection). You can still call any jQuery method on it, but without being tied to a DOM element what you can do is very limited. It will have .length of zero, and the callback will not be reached when iterating over with .each.
I would check the length of the jQuery object since jQuery will always return an object.
it will be always a parent, can be the body for example
use jquery data for example to set your init marker
var wasInit = ( $(this).data("mypluginwasinit") !== undefined );
if(wasInit) return;
$(this).data("mypluginwasinit","yes");

Is a Function Argument a Native Element or a jQuery Set?

I'm writing a general purpose helper function in JavaScript. It accesses the className property of the element.
But since I'm using jQuery, it turns out that sometimes the element passed as an argument is actually an array (a jQuery set).
What is the simplest way to use an argument if it's an element or get the first element from the set if the argument is a set? Does jQuery have a tool for this?
You can test if the object is a jQuery object by using instanceof:
if (myObject instanceof jQuery) {
// use $.each to loop over myObject and get class names of all
// or just use myObject[0].className to return only the first
} else {
// it's not a jQuery object
}
You always want a native element? Each jQuery object has a property jquery (containing the jQuery version) so you can use it to check if the element you got is likely to be a jQuery object:
obj = obj.jquery ? obj[0] : obj;
If you are not into ducktyping:
obj = (obj instanceof jQuery) ? obj[0] : obj;
In case you want to pass a single element, an array of elements or a jQuery object to the same method, do the following:
function stuff(element) {
element = [].concat(element)[0];
}
If you have to deal with both jQuery objects and non-jQuery elements, this is a bit tougher. Keep in mind that the jQuery collection can possibly have different classNames for each element, i.e. the first and others may not be the same.
It's slightly inefficient, but this will work:
function (obj) {
var className = $(obj).get(0).className;
}
Wrapping a jQuery object in jQuery() does nothing, but wrapping the element allows you to access it again via .get. It's inefficient because you may have an extra $() call.
If that's no good, you could check obj.hasOwnProperty('className') since a jQuery object usually should not have it, but I think that is riskier.
Is a jQuery Object an Element or an Array of Elements?
Strictly, neither.
A jQuery object is a Javascript object that has some of the Array capabilities. In most aspects you can use it as an array of elements.
If the jQuery object may contain several elements, and you only want the first, you can use the first method to get that:
elements = elements.first();
If the parameter can be either an element, an array of elements, or a jQuery object, you can wrap it in a jQuery object to handle all the cases:
elements = $(elements).first();

$.get sends prototype functions in request URL?

I have some prototype functions added to Object which in my opinion were practical in certain scenarios. However, I noticed that when I executed a $.get, the prototype functions are handled as data members and are sent like http://...?prototypefunc=false.
This is rather useless as I don't supply these as data members, but they are added to the query string.
To be exact, I have this code:
Object.prototype.in = function() {
for(var i=0; i<arguments.length; i++)
if(arguments[i] == this) return true;
return false;
}
$.get('http://localhost/test.php', {'test': 'foo'}, function(text) { });
The corresponding URL constructed is:
http://localhost/test.php?test=foo&in=false
How can I avoid this?
jQuery runs a for...in loop on the object passed, which iterates over all enumerable properties of an object, whether inherited or not. It doesn't do any checks to see if the object's property is owned by the object or inherited via the prototype chain.
Also, if it encounters a property whose value is a function during the serialization of the object it will execute the function and use the return value.
There are a few solutions:
Change your code so that it doesn't modify Object.prototype.
Pass a string instead of an object, or serialize the object to a string using your own code.
Override the jQuery.param() function with your own, and force it to check each property with .hasOwnProperty(propertyName).
Make the property on the prototype chain non-enumerable by using Object.defineProperty() (ECMAScript 5 compliant browsers only).
In your case, if you dont't want to remove that in function, you should serialize data (with function, which works similary to jQuery serialize) to string and append it to URL.

Categories