jQuery object plugin is not persistant - javascript

I am trying to extend a jquery element with custom properties and functions so I can track my elements and get their custom properties anytime.
at the moment I have done this:
jQuery.fn.designerElement = function (tpl, cls) {
this.__template = tpl
this.des__class = cls;
this.compile = function () {
this.html(this.__template(this));
return this;
}
return this;
};
var template = Handlebars.compile(
'<div id="' + +new Date() + '" class="{{des__class}}"></div>'
);
var el = $('<div></div>').designerElement(template, "form-container");
el.attr('id', "test");
el.compile();
$('body').append(el);
Now if I call $('#test').compile() it say says the method is undefined.
Here is a fiddle: http://jsfiddle.net/jmorvan/HLVj4/
To explain my context, I need the methods and properties available directly on the object for some dataBindings to work, thats why i can't use .data(). It seemed to me jquery plugin would be the best approach but I am definitly missing something here.
So just to be clear I would need to be able to access properties like this: $('#test').__template as well as functions.
thanks for your time!

I think you already know what is going on but here's a short explanation: With $('#test') you are creating a new jQuery object. It contains the same element but you are defining properties for the jQuery object, not the element. So what you are asking is if there is a way to add the functionality to the element but with the jQuery object attached. In jQuery data() can after all be used to do this:
jQuery.fn.designerElement = function (tpl, cls) {
this.__template = tpl
this.des__class = cls;
this.compile = function () {
this.html(this.__template(this));
return this;
}
this.data('this', this);
return this;
};
And retrieve that object with:
var test = $('#test').data('this');
Here is the fiddle
Another solution is to store all these objects in a globally available array or JSON object.

Related

Javascript: run object method on DOM object selected through other property

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>

Functions in javascript

I am new to javascript and I was messing around with it. I was checking out jquery and I wanted to see if I could create something to do the same things. This is my js file
//returns a dom
var $ = function(selector) {
return document.querySelector(selector);
};
//adds something to the dom
var append = function(value) {
this.innerHTML += value;
};
//clears dom
var empty = function() {
while (this.lastChild) this.removeChild(this.lastChild);
};
When I call $('#test') I get the dom with id test. When I call $('#test').append('hi there') it works. However when I try $('#test').empty() I get a Uncaught TypeError: $(...).empty is not a function Any idea why? If I am doing something comletely wrong please let me know. Thanks
Your functions aren't added to the prototype chain of DOM elements, (that wouldn't be a good idea), so they can't be called as methods of DOM elements.
append works, because the DOM node already had a method called append, and it has nothing to do with the function you stored in a variable called append.
jQuery works by creating a container object that holds a collection of DOM elements and has it's own methods. EG:
var $ = function(selector) {
var objects = document.querySelectorAll(selector);
return {
append: function ( ) {
// do something to `objects` here
},
empty: function ( ) {
},
};
};

dynamic call to a function jQuery

I am trying to build a lib and I need to call functions dynamically depending on the variables I have in parameter like this
strategies = min
function dispatchRuleToStrategy(strategies)
{
$.each(strategies, function(index, value) {
strategy = "strategy_" + value;
});
}
function strategy_min()
{
// do something
}
How can I call the function strategy_min() from dispatchRuleToStrategy()?
I've been trying a couple of things none of which are working.
Thanks for your help
Use an Object to create a dictionary of your functions e.g. lib
var lib = {
'strategy_min': strategy_min
};
then you can invoke via the key in this dictionary Object
lib['strategy_min']();
If you've named all your functions and you don't want to re-type the names over and over, you could
var lib = {};
function addToLib(fn) {
lib[fn.name] = fn;
}
// then
addToLib(strategy_min);
// or
[strategy_min].forEach(addToLib);
Put them in an object and use the property name:
var strategy_table = {
min: function() {
// do something
},
max: function() {
// do something else
},
...
};
Then you can access them as strategy_table[value]:
$.each(strategies, function(index, value) {
strategy_table[value]();
});
Others have already suggested to create a wrapper object for the functions, however, if your strategy_min() function is in the global scope, you can access it directly:
window['strategy_' + value]();
window in browsers refers to the global object. The bracket notation is used to access properties whose keys are dynamically generated. This way you are accessing the function, which is a property of the global object, i.e. window, and calling it using the parentheses.
Finally I found the real problem. I was in a jquery document ready which is a closure. I did not knew what closures were before today.
Thanks all for your help
You can use eval() function in the following manner
$.each(strategies, function(index, value) {
strategy = "strategy_" + value;
eval(strategy+"()");
});

Redefining a jQuery function

I need to redefine the jQuery val() function, but I don't want to mess with the source code. Is it possible to modify it on just one element?
Specifically, I need to do something like this:$("div.smth").val = function() {return this.innerText;};. However, the val function is not modified by this code. What am I doing wrong?
You should instead modify the function of the prototype (jQuery calls this fn). This is where all functions like $(...).something inherit from.
$.fn.val = function() { ... };
If you want to save the original function:
var old = $.fn.val; // `old` won't be overwritten
$.fn.val = function() { ... };
This will do what you want, you need to attach your new val method to jQuery's plugin stack:
$.fn.val = function(value) {
return this[0].innerText;
}
The other answers indicate how to replace the .val() method, but if you know you only need this for one specific element can't you just do this:
$("div.smth")[0].innerText
But in any case isn't that pretty much what the existing jQuery .text() method does?
jsval: function(fn) {
var that = this;
var newfn = function(event) { fn.apply(that, arguments); };
this.click(newfn);
return newfn;
}
Instead now you can call your normal val and on that specific div, call jsval

JS chaining pattern to insert elements into the DOM

I am struggling to make the pattern below work. I'm not interested in using a library.
function _createElement(tagNm, attr){
var el = document.createElement(tagNm);
for(var at in attr){
el.setAttribute(at, attr[at]);
}
}
//below function is not correct just for giving you idea
function _append(ele){
this.appendChild(ele)
return this;
}
// I would like to be able to achive following chaining patter with this.
var div = document.getElementById('div');
div._append( _createElement('div', {
id : 'parent', className: 'parent'
})).appendChile( _createElement('div', {
id : 'child', className: 'child'
}));
For something like that to work, you're going to have to have some sort of object to be the focal point of the chained calls. The object would be the value of this, in other words, in your "_append" function. An alternative would be to have your functions extend the native DOM object prototypes, but that won't work in older IE versions (and maybe not even newer ones; I'm not sure).
You could perhaps root everything in a wrapper around document.getElementById(). You'd set up a "class" object for all your append() and appendChild and whatever other functions you'd like to gather up for this facility. Those functions would all go on the prototype for that:
function chainingWrapper(elem) {
this.targetElement = elem;
}
chainingWrapper.prototype = {
append: function(arg) {
// do the append operation
return this;
},
appendChild: function(arg) {
// do the appendChild operation
return this;
},
// ...
};
Now you'll have a utility to start things off:
function forElement(id) {
return new chainingWrapper(document.getElementById(id));
}
So then you could do:
var div = forElement("myDiv");
div.appendChild(xyz).append(abc);
Inside the functions like "append" you'll be able to refer to that <div> as this.targetElement. There are a zillion other interesting things you could do with this sort of structure, of course.
The issue that you're having is that div (a DOM element) doesn't have a method called _append. You can't reliably modify the prototype of DOM elements, so you can't add the method (and it would be a bad idea even if you could).
Instead, you need to create a wrapper object, and create the append method on that:
function Appender(id) {
if (!(this instanceof Appender)) { // if the new keyword wasn't used
return new Appender(el);
}
this.element = document.getElementById(id); // create an instance variable of the element with the id passed
this.append = function(child) { // proxy the appendChild function
this.element.appendChild(child);
return this; // return the Appender object for chaining
};
}
You could then use this as follows:
Appender('div').append(_createElement('div', {
id : 'parent', className: 'parent'
})).append( _createElement('div', {
id : 'child', className: 'child'
}));
NB If you want to follow this approach, you're going to need to learn quite a bit more about Javascript's object model.
Even if you don't want to use it, you should defently take a look at some JavaScript library, like jQuery. They do exactly what you try to achieve.
jQuery does this, by putting the DOMElement inside a Wrapper and then call functions on that wrapper. These functions manipulate the given DOMObject underneath.
Your example would look like this:
var div = document.getElementById('div');
$(div).append( _createElement('div', {id : 'parent', className: 'parent'})...
And your try to implement jQuery's "$" function. The code can be found here.
It's possible to extend the DOM. Here's a working example:
HTMLElement.prototype.append = function(ele) {
return this.appendChild(ele); // returns the new child
}
function _createElement(tagNm, attr){
var el = document.createElement(tagNm);
for (var at in attr) {
el.setAttribute(at, attr[at]);
}
return el;
}
var div = document.getElementById('myDiv');
div.append(_createElement('div', {
id : 'parent', className: 'parent'
})).appendChild( _createElement('div', {
id : 'child', className: 'child'
})).appendChild(document.createTextNode("test"));
But it's probably not a good idea:
In Javascript, can you extend the DOM?
http://perfectionkills.com/whats-wrong-with-extending-the-dom/
What others are saying is correct. You need to some how add the method _append to your object. For example, jQuery returns a jQuery object after each call. The jQuery object has functions like .each, .ajax etc etc. It's a big wrapper.
Something like this will get you the chaining you're looking for:
function _createElement(tagNm, attr){
var el = document.createElement(tagNm);
for(var at in attr){
el.setAttribute(at, attr[at]);
}
return el;
}
function customObj(Id) {
var obj = document.getElementById(Id);
obj._append = function(el) { this.appendChild(el); return this; }
return obj;
}
// I would like to be able to achive following chaining patter with this.
var div = customObj('test');
div._append( _createElement('div', {
id : 'parent', className: 'parent'
}))._append( _createElement('div', {
id : 'child', className: 'child'
}));

Categories