Obtain Javascript object from the dom element in google closure - javascript

Is there a way to obtain the underlying object from dom element.
For instance.
I have a base class which renders some buttons using
goog.ui.CustomButton.
In the child class I want to get underlying goog.ui.CustomButton
object through the generated dom element.
Is it possible or do I need to cache the underlying objects:
mylib.Base = function() {
};
mylib.Base.prototype.renderButtons = function(buttonTypes) {
for(var i in buttonTypes) {
var button = new goog.ui.CustomButton();
var buttonContainer = goog.dom.getElement('button-container_' + buttonTypes[i]);
button.render(buttonContainer);
}
};
mylib.Child = function() {
mylib.Base.base(this, 'constructor');
};
goog.inherits(mylib.Child, mylib.Base);
mylib.Super.prototype.someTask = function() {
this.renderButtons(['save_button','cancel_button']);
var saveButtonHolder = goog.dom.getElement('button-container_' + 'save_button');
var saveButtonElement = saveButtonHolder.childern[0]; // Not production code
// Now Is it possible to get the goog.ui.CustomButton object through the
// saveButtonElement ?
};

Related

Can I add/declare properties to a function in JavaScript?

I'm developing a RPG using HTML, CSS and JavaScript with jQuery as a personal project. Right now I'm doing the HUD where the player will have buttons to display information such as the Character Information, inventory, etc. Each of these buttons on will create a div where the info required will be displayed, if clicked again and the div is open it will delete it (or close it).
As far as I understand, functions in JS are treated as objects and thus properties can be added, I want to add a boolean property to the function that creates the div as a flag to check if the window is open or closed so I don't have to declare a global variable as flag to each button.
How do I declare those properties in a function?
Example:
let windowCharInfo = () => {
this.opened = false;
this.windowDisplay = function() {
$('<div>', {
class: 'HUDWindow',
id: 'charInfoWindow'
}).appendTo('#charInfo'); // Here's the window that will be created
// Some other code to add elements to that window will be here
}
}
The let windowCharInfo() is already an object or do I have to store it in a variable using 'new' keyword?
Also, windowCharInfo() will be called when the user clicks '#charInfo' (using onclick: 'windowCharInfo()')
Here is a Simple Constructor:
function Player(type) {
this.type = type;
this.weapons = []; // public
var thaco; // private
this.setTHACO = function(thaco) {
this.thaco = thaco;
return this;
}
this.getTHACO = function() {
return this.thaco;
}
this.addWeapon = function(weapon) {
this.weapons.push(weapon);
return this;
}
}
var player1 = new Player('elf'); // now it's an Object
player1.addWeapon('sword').addWeapon('axe').setTHACO('18');
console.log(player1.type);
var weapons1 = player1.weapons;
for (var i = 0, l = weapons1.length; i < l; i++) {
console.log(weapons1[i]);
}
console.log(player1.getTHACO());

I try to write the java script code like oops how to extend the DOM elements?

In my code like ,
function ElementBase(name) {
this.tagName = typeof name != "" ? name : 'div';
this.createElem();
}
ElementBase.prototype = {
createElem: function() {
this.elem = document.createElement(this.tagName);
},
getIndex: function() {
var nodes = this.elem.parentNode.childNodes,
node;
var i = count = 0;
while ((node = nodes.item(i++)) && node != this.elem)
if (node.nodeType == 1) count++;
return (count);
}
};
I try to create the DOM element tag is "div".
function Div() {
this.tagName = 'div'
ElementBase.call(this, this.tagName);
}
Div.prototype = Object.create(ElementBase.prototype);
My Question is,
1) How to access the getIndex function from the html document after inserting the created objects?
example:
var div = new Div();
div.id = "d1"
document.body.appendChild(div.elem);
// After div.getIndex() working
Then some situation i need the index value of that div (id="d1") element from document.
var d= document.getElementById("d1");
d.getIndex() //not working
What mistakes i did it in above code?
thanks advance..
I think when you do document.body.appendChild(div.elem) you just do document.body.appendChild(document.createElement('div')) nothing more.
And when you do var d= document.getElementById("d1"); d is just an object return from the DOM that has nothing to do with your var div
what you can do is:
Div.prototype.getIndex.call(d);
But that doesn't actually extend your object. Actually extending a DOM object is a bad practice (check this http://perfectionkills.com/whats-wrong-with-extending-the-dom/).
Look closely at your code.
div is an instance of Div and it has a property .elem that holds the actual DOM element.
So when you do div.id = "d1", you are not setting the id of the DOM element.
var div = new Div();
div.id = 'd1'; // <div></div>
div.elem.id = 'd1'; // <div id="d1"></div>
But there's one more problem: when you do d= document.getElementById("d1"), what you get is a DOM element, not an instance of Div().
Since .getIndex() is defined on .prototype of Div(), plain old DOM elements don't have access to it.
How you solve this situation depends on what exactly you need to accomplish with your code.
Edit 1: In response to OP's comment:
document.getElementById() returns an instance of HTMLDivElement, which is fundamentally different from an instance of Div.
One solution is to use a setter method:
function Div() {
// ...
}
Div.prototype.setId = function setId(id) {
this.elem.id = id;
}
var div = new Div();
div.setId('d1'); // same as doing div.elem.id = 'd1';
another solution is to use id in the constructor function itself:
function Div(id) {
// ...
this.elem.id = id; // or you can use "this.setId(id)"
/*
if "id" is provided,
it will take that value,
else it is set to "undefined",
which is the same as not being set
*/
}
Div.prototype.setId = function setId(id) {
this.elem.id = id;
}
var div = new Div('d1'); // same as doing div.elem.id = 'd1';
div.setId('d2'); // same as doing div.elem.id = 'd2';

How to Associate an Object with a DOM Element

I have a master object in my JS setup, i.e.:
var myGarage = {
cars: [
{
make: "Ford",
model: "Escape",
color: "Green",
inuse: false
},
{
make: "Dodge",
model: "Viper"
color: "Red",
inuse: true
},
{
make: "Toyota",
model: "Camry"
color: "Blue",
inuse: false
}
]
}
Now I loop over my cars and put them in a table. In the table I also have a button that lets me toggle the car as "in use" and "not in use".
How can I associate the DOM Element of every row with its corresponding car, so that if I toggle the "inuse" flag, I can update the master object?
You can actually attach an object directly to a node:
var n = document.getElementById('green-ford-escape');
n.carObject = myGarage.cars[0];
n.onclick = function() {
doSomethingWith(this.carObject);
}
For the same of removing ambiguity, in some cases, it's more clear write the above event handler to refer to n instead of this:
n.onclick = function() {
doSomethingWith(n.carObject);
}
You can also refer directly to the object from the attached event:
var n = document.getElementById('green-ford-escape');
n.onclick = function() {
doSomethingWith(myGarage.cars[0]);
}
In the latter case, myGarage does not have to be global. You can do this and expect it to work correctly:
(function(){
var myGarage = { /* ... etc ... */ };
var n = document.getElementById('green-ford-escape');
n.onclick = function() {
doSomethingWith(myGarage.cars[0]);
}
})();
The node's event function will "hold onto" the local variable correctly, effectively creating a private variable.
You can test this in your Chrome/FF/IE console:
var o = {a: 1};
var n = document.createElement('div');
n.innerHTML = "click me";
n.data = o;
n.onclick = function() { n.data.a++; console.log(n.data, o); }
document.body.appendChild(n);
You should see the console log two identical objects with each click, each with incrementing a values.
Beware that setting n.data to a primitive will not create a reference. It'll copy the value.
I'd suggest considering addEventListener, and a constructor that conforms your objects to the eventListener interface.
That way you can have a nice association between your object, your element, and its handlers.
To do this, make a constructor that's specific to your data.
function Car(props) {
this.make = props.make;
this.model = props.model;
// and so on...
this.element = document.createElement("div"); // or whatever
document.body.appendChild(this.element); // or whatever
this.element.addEventListener("click", this, false);
}
Then implement the interface:
Car.prototype.handleEvent = function(e) {
switch (e.type) {
case "click": this.click(e);
// add other event types if needed
}
}
Then implement your .click() handler on the prototype.
Car.prototype.click = function(e) {
// do something with this.element...
this.element.style.color = "#F00";
// ...and the other properties
this.inuse = !this.inuse
}
So then you can just loop over the Array, and make a new Car object for each item, and it'll create the new element and add the listener(s).
myGarage.cars.forEach(function(obj) {
new Car(obj)
})
You can use HTML5 data-* attribute to find out which row it is. You must be doing something like this
var table = $('<table>'); // Let's create a new table even if we have an empty table in our DOM. Simple reason: we will achieve single DOM operation (Faster)
for (var i=0; i<myGarbage.cars.length; i++) {
// Create a new row and append to table
var tr = $('<tr>').appendTo(table);
var carObject = myGarbage.cars[i];
// Traverse the JSON object for each car
for (var key in carObject) {
// Create other cells. I am doing the last one
var td = $('<td>').appendTo(tr);
var button = $('<button>').attr('data-carId', i).addClass('toggle-inuse').appendTo(td);
}
}
// If en ampty table awaits me in DOM
$('#tableId').html(table.html());
Now we will add event listener on button :-
$('.toggle-inuse').click(function() {
var i = $(this).data('carId');
myGarbage.cars[i].inuse = !myGarbage.cars[i].inuse; //Wow done
}
Try this out !!
You'll want some sort of ID or distinct row in your information, else you'll have to rely on the array index to do this. Either way you'll want to store the data using data attributes.
So when you loop through:
for (var i = 0, l = array.length; i < l; i++) {
var div = '<tr data-car="' + JSON.stringify(array[i]) + '" data-index="' + i + '"><td></td></tr>'
}
And on your click event:
$('button').click(function() {
var carIndex = $(this).closest('tr').attr('data-index');
var carData = $(this).closest('tr').attr('data-car');
if (carData) carData = JSON.parse(carData);
myGarage.cars[carIndex].inUse = true;
})
If you bind the data to the DOM, you may not even need to update the actual JS data. Could go over each row in the table and re-create the data-object you created the table from.

Returning a reference in JavaScript

Here is the code:
http://jsfiddle.net/GKBfL/
I am trying to get collection.prototype.add to return a reference such that the final alert will display testing, testing, 123, testing. Is there a way to accomplish what I'm trying to do here?
HTML:
<span id="spantest">testing, testing, 123, testing</span>​
JavaScript:
var collection = function () {
this.items = {};
}
collection.prototype.add = function(sElmtId) {
this.items[sElmtId] = {};
return this.items[sElmtId];
}
collection.prototype.bind = function() {
for (var sElmtId in this.items) {
this.items[sElmtId] = document.getElementById(sElmtId);
}
}
var col = new collection();
var obj = {};
obj = col.add('spantest');
col.bind();
alert(obj.innerHTML);​
You problem is this line:
this.items[sElmtId] = document.getElementById(sElmtId);
This overwrites the object currently assigned to this.items[sElmtId] with the DOM node. Instead, you should assign the node to a property of that object:
this.items[sElmtId].node = document.getElementById(sElmtId);
That way, obj.node will always refer to the current node:
alert(obj.node.innerHTML);
DEMO
Side note: The problem with your fiddle is also that you execute the code when the DOM is not built yet (no wrap (head)), so it cannot find #spantest. You have to run the code once the DOM is ready, either no wrap (body), onDomRead or onLoad.
Creating a reference like you need is impossible in JavaScript. The closest thing you can get is either a nested or closed object, or just copying it over, like so:
var collection = function() {
this.items = {};
};
collection.prototype.add = function(sElmtId) {
return this.items[sElmtId] = {};
};
collection.prototype.bind = function() {
for(var sElmtId in this.items) {
var element = document.getElementById(sElmtId);
for(var x in element) {
this.items[sElmtId][x] = element[x];
}
}
};
var col = new collection();
var obj = {};
obj = col.add('spantest');
col.bind();
alert(obj.innerHTML);
But it won't be truly "bound". You'll have to use nested objects if you need that kind of functionality, and it will probably defeat the point of your syntactic sugar.
http://jsfiddle.net/GKBfL/7/

Unique html item ID in Javascript

I am building a drag'n'drop gui builder in Javascript. So far so good.
As I add items to the GUI and configure them; I have two mechanisms for addressing them:
the 'class' - which I use for doing things to all instances of an item (eg CSS, generic functionality and so on and so forth) and which I can bind javascript libraries to... and I can make full use of polymorphic class names (ie class="name1 name2 name3 name4" with different things bound to each class name...)
the 'id' - which refers to this particular instance of a text box or a paragraph and which I can bind javascript libraries to
My problem is this: the 'id' must be unique across all html items on the page (by definition) so how do I ensure this? I need to get all the id's of all the items and then maintain some sort of state table.
Starting from a blank bit of html this is pretty reasonable - but I need to start from a partly created bit of html with a mixture of existing 'id's - some of which will be in my unique scheme and some of which wont be...
The way to do this best ought to be a solved problem.
Suggestions, tips, examples?
The best way to do this will depend entirely upon the structure and organization of your javascript. Assuming that you are using objects to represent each of your GUI elements you could use a static counter to increment your ids:
// Your element constructor
function GuiElement() {
this.id = GuiElement.getID();
}
GuiElement.counter = 0;
GuiElement.getID = function() { return 'element_' + GuiElement.counter++; };
Of course you probably have more than one type of element, so you could either set each of them up so that they have their own counter (e.g. form_1, form_2, label_1, label_2) or so that they all share a counter (e.g. element_1, element_2, element_3), but either way you will probably want them to inherit from some base object:
// Your base element constructor
function GuiElement(tagName, className) {
this.tagName = tagName;
this.className = className;
}
GuiElement.counter = 0;
GuiElement.getID = function() { return 'element_' + GuiElement.counter++; };
GuiElement.prototype.init = function() {
this.node = document.createElement(this.tagName);
this.node.id = this.id = GuiElement.getID();
this.node.className = this.className;
}
// An element constructor
function Form() {
this.init();
}
Form.prototype = new GuiElement('form', 'form gui-element');
// Another element constructor
function Paragraph() {
this.init();
}
Paragraph.prototype = new GuiElement('p', 'paragraph gui-element');
You could also go this route if you would rather keep some variables "private":
// Your element constructor constructor
var GuiElement = (function() {
var counter = 0;
function getID() {
return 'element_' + counter++;
}
return function GuiElement(tagName, className) {
return function() {
this.node = document.createElement(tagName);
this.node.id = this.id = getID();
this.node.className = className + ' gui-element';
this.className = className;
};
}
})();
// Create your element constructors
var Form = GuiElement('form', 'form'),
Paragraph = GuiElement('p', 'paragraph');
// Instantiate elements
var f1 = new Form(),
f2 = new Form(),
p1 = new Paragraph();
Update: If you need to verify that an id is not already in use then you could add the check you and of the getID methods:
var counter = 0;
function getID() {
var id = 'element_' + counter++;
while(document.getElementById(id)) id = 'element_' + counter++;
return id;
}
function uniqueId() {
return 'id_' + new Date().getTime();
}
If you happen to be using the Prototype library (or want to check it out), you can use the Element.identify() method.
Otherwise, Darin's response is a good idea as well.
function generateId() {
var chars = "0123456789abcdefghiklmnopqrstuvwxyz",
string_length = 8,
id = '';
for (var i = 0; i < string_length; i++) {
var rnum = Math.floor(Math.random() * chars.length);
id += chars.substring(rnum, rnum + 1);
}
return id;
}
Close enough to unique is good enough. Don't use the Date() solution unless you're only generating a single ID at any given time...

Categories