Say I create a javascript class as follows:
class DivClass {
constructor () {
this.div1 = document.createElement('div')
this.div1.id = 'div1'
}
}
Later I instantiate the class as follows:
var divObject = new DivClass()
parentDiv.appendChild(divObject.div1)
and the DIV eventually appears in the DOM.
If I was to locate the 'div1' element within the DOM, say via getElementById() for argument sake, is there anyway of getting back to the javascript 'divObject' responsible for its creation?
From what little I've learned about javascript, I kind of get the impression that the translation from javascript API to DOM is a one way trip and this just simply isn't possible.
Apologies in advance if I've gotten any of the terminology wrong, but I'm kind of new to javascript and still don't fully understand the DOM/API relationship.
Any advice would be greatly appreciated.
You can add a reference to the object to the DIV element.
class DivClass {
constructor () {
this.div1 = document.createElement('div')
this.div1.id = 'div1'
this.div1.divClass = this;
}
}
Then you can use document.getElementById("div1").divClass to get the object.
Related
I'm trying to make an object that creating an instance of it would act like document.createElement(), with the added benefit that classes would be able to extend from it, plus pass to it a parent element that it would automatically appendChild itself to.
For example:
class Player extends Model {
constructor(parent, att0, att1) {
super(parent, "div");
let attribute0 = new Model(this, "h5");
let attribute1 = new Model(this, "h5");
attribute0.innerText = att0;
attribute1.innerText = att1;
}
}
Instead of:
class Player{
constructor(att0, att1) {
let attribute0 = document.createElement("h5");
let attribute1 = document.createElement("h5");
attribute0.innerText = att0;
attribute1.innerText = att1;
this.appendChild(attribute0);
this.appendChild(attribute1);
}
}
(And appending Player instances as children to their parent class)
Is this possible to do? If so, how and what's the best way to implement it? If not, is there at least similar I could do to achieve a similar result?
Thanks in advance.
The web actually supports custom elements, you can do:
class Player extends HTMLDivElement {
constructor() {
this.appendChild(document.createElement('h2'));
this.lastElementChild.textContent = this.dataset.title;
this.appendChild(document.createElement('h4'));
this.lastElementChild.textContent = this.dataset.subtitle;
}
}
customElements.define('player-wrapper', Player, { extends: 'div' });
And then render it with:
<player-wrapper subtitle='foo' title='bar' />
You can read about it on MDN.
You can also take children as attributes etc though declarative composition is usually done in the templates which are supported anyway.
Typically - people just use some component system like Angular/React/Vue for this though rather than using "raw" web components.
I'm tinkering with writing a more efficient methodology in the creation of dynamically generated DOM elements via JavaScript. This is something I intend to add into my own JS framework later on. Looking for other OOP devs that could help better refine what I do have.
Here's a link to the working CodePen:
http://codepen.io/DaneTheory/pen/yeLvmm/
Here's the JS:
function CreateDOMEl() {};
CreateDOMEl.prototype.uiFrag = document.createDocumentFragment();
CreateDOMEl.prototype.elParent = function(elParent, index) {
this.elParent = document.getElementsByTagName(elParent)[index];
}
CreateDOMEl.prototype.elType = function(type) {
newEl = document.createElement(type);
this.uiFrag.appendChild(newEl);
}
CreateDOMEl.prototype.elContent = function(elContent) {
this.elContent = elContent;
newEl.textContent = elContent;
}
CreateDOMEl.prototype.buildEl = function() {
this.elParent.appendChild(this.uiFrag);
}
var div = new CreateDOMEl();
div.elParent('body', 0);
div.elType('DIV');
div.elContent('OK');
div.buildEl();
console.log(div);
var bttn = new CreateDOMEl();
bttn.elParent('body', 0);
bttn.elType('BUTTON');
bttn.elContent('SUBMIT');
bttn.buildEl();
console.log(bttn);
And some CSS to get elements to appear on page:
div {
width:100px;
height:100px;
border: 1px solid red;
}
My thoughts:
For performance, using the prototype to build methods versus placing all the logic in the constructor.
Rather than directly appending elements to the page, append to a single Document Fragment. Once the element is built out as a Doc Frag, appending the Doc Frag to to the DOM. I like this method for performance, but would like to improve upon it. Any useful implementations of requestnimationFrame, or using range and other versions of the document fragment method?
Silly, but I think for debugging it'd be nice to see the generated Element type within the Object property's on console log. As of right now, console logging a created element will show the elements parent and text content. It'd be great to show the elements type as well.
Creating more than one element at a time is another piece of functionality I'd like to offer as an option. For instance, creating a div element creates one div element. What's a good way to add another optional method to create multiple instances of div's.
div.elType('DIV');
// After calling the elType method, do something like this:
div.elCount(20);
// This would create 20 of the same divs
Lastly, a nice clean way to optionally add attributes (i.e: classes, an ID, value, a placeholder, custom attributes, data-* attributes, etc.). I've got a nice helper function I use that adds multiple attributes to an element in an object literal syntax looking way. Adding this as a method of the constructor would be ideal. Here's that function:
function setAttributes(el, attrs) {
for(var key in attrs) {
el.setAttribute(key, attrs[key]);
}
}
// A use case using the above
// function would be:
var anInputElement = document.createElement("TEXTAREA");
setAttributes(anInputElement, {
"type": "text",
"id": "awesomeID",
"name": "coolName",
"placeholder": "Hey I'm some placeholder example text",
"class": "awesome"
});
// Which creates the following HTML snippet:
<textarea type="text" id="awesomeID" name="coolName" placeholder="Hey I'm some placeholder example text" class="awesome">
As a side note, realizing now that the above helper function needs rewritten so that multiple classes could be created.
Respectfully, I believe you may be overthinking it. Just use the tools available in JavaScript and get 'er done. In terms of performance, computers are so fast at running your JavaScript that you (and me) are unable to perceive, or even comprehend, the speed. Here's how I add a link to an MDL nav menu, for example. It's just vanilla JS. Don't forget to add event listeners.
function navMenuAdd(type,text){
var newAnchor = doc.createElement("anchor");
newAnchor.classList.add('mdl-navigation__link');
newAnchor.classList.add(type);
newAnchor.href = "javascript:void(0)";
var anchorContent = doc.createTextNode(text);
newAnchor.appendChild(anchorContent);
newAnchor.addEventListener('click', navMenuClickHandler, false);
//newAnchor.style.display = 'none';
if (type === 'Thingy A'){
//insertAfter(newAnchor, navMenuCredentials);
navMenuCredentialsPanel.appendChild(newAnchor);
} else if (type === 'Thingy B'){
//insertAfter(newAnchor, navMenuDevices);
navMenuDevicesPanel.appendChild(newAnchor);
}
}
I have the following little piece of code:
var instance = this;
window.onload = function () {
for (var i = 0; i < array.length; ++i) {
var currentDivId= array[i];
var currentDiv = document.getElementById(currentDivId);
try {
if (!currentDiv) {
throw 'Div id not found: ' + currentDivId;
}
var image = document.createElement('img');
image.src = 'img.jpg';
image.onclick = function() {
instance.doSomething(currentDivId);
};
currentDiv.appendChild(image);
}
catch(e) {
console.warn('oops');
}
}
};
This code is passed an array of id of divs. What it does is that, it renders an image at each of those divs and set their onclick property.
Say I have an array of strings: ['abc', 'xyz']
I want the code to place an image inside <div id="abc"></div> and another image inside <div id="xyz"></div>.
When you click the first image, instance.doSomething function should be called with parameter 'abc' and vice versa.
But the code does not work as expected. It always calls instance.doSomething with the last parameter in the array, in this case, 'xyz'.
I'm new to JS and still don't have a solid grasp of its inner workings. What's wrong here and how can I fix it?
Any help appreciated.
image.onclick = function() {
instance.doSomething(this.parentNode.id);
};
That should do it. Since we know that the image is inside the div we want to get at, just go one dom element up and get its id.
Welcome to the wonderful world of Javascript scoping issues. As it stands now, JS is treating your onclick code as something like "when this object is clicked, fetch the value stored in the currentDivID variable AT THE TIME THE CLICK occurs and pass it to the doSomething function".
What you should do is base the argument on the image object itself. Every DOM object knows where it is in the DOM tree, so at the time it's clicked, the onclick code should use DOM traversal operations to figure out which div it's inside of and dynamically retrieve its ID. That way you don't have to worry about binding variables and scoping issues... just figure out which div contains your image and get the ID at run time.
Try:
image.onclick = (function() {
var currentD = currentDivId;
return function() {
instance.doSomething(currentD);
}
})();
Hope it helps
Is it possible to instantiate an element on Mootools based on the automatic UID that mootools create?
EDIT: To give more info on what is going. I'm using https://github.com/browserstate/history.js to make a history within an ajax page. When I add a DOM element to it (which does not have an id), at some point it passes through a JSON.toString methods and what I have of the element now is just the uid.
I need to recreate the element based on this UID, how could I go about doing that? Do I need to first add it to the global storage to retrieve later? If so, how?
in view of edited question:
sorry, I fail to understand what you are doing.
you have an element. at some point the element is turned into an object that gets serialised (all of it? prototypes etc?). you then take that data and convert to an object again but want to preserve the uid? why?
I don't understand how the uid matters much here...
Using global browser storage also serialises to string so that won't help much. Are we talking survival of page loads here or just attach/detach/overwrite elements? If the latter, this can work with some tweaking.
(function() {
var Storage = {};
Element.implement({
saveElement: function() {
var uid = document.id(this).uid;
Storage[uid] = this;
return this;
}
});
this.restoreElement = function(uid) {
return Storage[uid] || null;
}
})();
var foo = document.id("foo"), uid = foo.uid;
console.log(uid);
foo.saveElement().addEvent("mouseenter", function() { alert("hi"); } );
document.id("container").set("html", "");
setTimeout(function() {
var newElement = restoreElement(uid);
if (newElement)
newElement.inject(document.body);
console.log(newElement.uid);
}, 2000);
http://jsfiddle.net/dimitar/7mwmu/1/
this will allow you to remove an element and restore it later.
keep in mind that i do container.set("html", ""); which is not a great practice.
if you do .empty(), it will GC the foo and it will wipe it's storage so the event won't survive. same for foo.destroy() - you can 'visually' restore the element but nothing linked to it will work (events or fx).
you can get around that by using event delegation, however.
also, you may want to store parent node etc so you can put it back to its previous place.
There may be a better way of doing this...so please suggest if there is.
I've got some javascript that calls an AIR function. This AIR functions creates a new HTML element and adds it to the "Stage" like so:
// guid is the ID given to the new window (HTML element) by javascript
private function createNewWindow(guid:String):void {
var frame:HTML = new HTML();
frame.id = guid;
addElement(frame);
}
Now I've also got a function that sets the location of the frame based on its id...this is where I'm struggling.
// set the location of the window referenced by it's id (guid)
private function setLocation(guid:String, location:String):void {
// psuedocode. Obviously it won't work.
stage.getById(guid).location = location;
}
So, how do I "get" my HTML element based on its ID?
Short answer, you don't. This isn't javascript, this is a OO language and as such, you need to change your thought process. What are you trying to do? Create several html windows within an air application? If you want to have an id based approach, you're going to need to store the id and the pointer to the component in an data structure (like a dictionary).
private var _components:Dictionary = new Dictionary();
this._components['someId'] = someComponent;
And from there you can add a function that just saves/returns the components. I'm not entirely sure what's your approach and what you're trying to accomplish, but my gut tells me you're not doing something right.