Can I find an object that owns an HTML element in Javascript? - javascript

Let's say I have some code like this:
let myList = new map();
let something = new objectMaker();
object.set(someIndex,new objectMaker());
function objectMaker(){
this.parentThing=new createElement("div");
this.childThing=new createElement("div");
this.parentThing.onclick=clickyFunction();
//See below for details of function call
this.parentThing.id=someIndex+"something";
}
Now, the problem is finding the object which owns the element when I call "clickyFunction()." My current solution looks something like: this.parentThing.onclick=function(){clickElement(event,this);}; If someone clicks childThing e.srcElement will reference childThing and the this parameter will refer to this.parentThing.
Thus, then I have to run a loop
function clickyFunction(e,callingObject) {
for (let key of myList.keys()) {
if (myList.get(key).parentThing.id==callingObject.id){
//This is the parent
...
Is there a way to just pass the instance of objectMaker to clickyFunction() (regardless of which)? It seems like this would be a more elegant solution. Or, should I make some overall change to how the program is organized?

You can use the 'bind' method to create a new function with the desired 'this' value and pass the instance of objectMaker to the clickyFunction.
For example, you can modify the line where you set the onclick handler like this:
this.parentThing.onclick = clickyFunction.bind(this);
By doing this, you're creating a new function that will call clickyFunction with the current instance of objectMaker set as the this value. Then, when you use the this keyword in clickyFunction, you can access the instance of objectMaker directly:
function clickyFunction(e) {
// `this` refers to the instance of objectMaker
...
}
As an alternative, you could pass the instance of objectMaker to the clickyFunction as an argument, like this::
this.parentThing.onclick = function(e) {
clickyFunction(e, this);
}
function clickyFunction(e, objectMakerInstance) {
// `objectMakerInstance` refers to the instance of objectMaker
...
}
Either way, you can skip the loop and access the desired instance of objectMaker directly.
*EDIT:
You can find the object that owns an HTML element in JavaScript using the ownerDocument property of the element. This'll give you the Document object that owns the element. Then, you can use the defaultView property of the document to get the Window object that owns the document, like:
let element = document.getElementById('my-element');
let ownerWindow = element.ownerDocument.defaultView;
Alternatively, you can use the parentNode property to navigate up the DOM tree and find the nearest ancestor element that has an ownerDocument property.
You can do this like this:
let element = document.getElementById('my-element');
let ancestor = element;
while (ancestor && !ancestor.ownerDocument) {
ancestor = ancestor.parentNode;
}
let ownerWindow = ancestor.ownerDocument.defaultView;
This can be handy if the element isn't directly owned by a document, but is instead owned by an element that is itself owned by a document, like a template element or a svg element.

Related

JavaScript Class with same name for property and method

When researching a problem I ran into when trying to remove an event listener that uses bind() similar to the syntax in use by the class constructor below (I am not trying to remove this one, but it would not be successful if I tried), I found two sources that show a solution to that similar to that in use by the onDown method below (I do want this one removed once no longer needed) that creates a separte function that does the binding and the addEventListener attaches the function by its name:
class TestClass {
constructor(el) {
el.addEventListener('mousedown', function () { this.onDown(el.id) }.bind(this));
}
onDown(id) {
// Here a property named onUp is created:
this.onUp = this.onUp.bind(this, id);
document.addEventListener('mouseup', this.onUp);
}
// Here a method named onUp is defined in the class:
onUp(id) {
document.removeEventListener('mouseup', this.onUp);
console.log("Mouse down on " + id + " and then mouse up.");
}
}
const d1 = new TestClass(document.getElementById("div1"));
const d2 = new TestClass(document.getElementById("div2"));
<div id="div1">Div 1: Click me and let go!</div>
<div id="div2">Div 2: Click me and let go!</div>
This code works and achieves my desired results, but what I observed is this creates a both property and a method with the same name, named onUp, in the objects created. This can also be verified by looking at the properties of the objects created (see image below). This is not otherwise possible as confirmed by the error received when calling the method that uses the same name as a property here:
class Test {
sameName = "property";
sameName() { return "method"; }
}
const testObject = new Test();
console.log(testObject.sameName); // Here Console outputs the value of the sameName property
console.log(testObject.sameName()); // This fails calling the sameName() method
Can someone help explain to me how this is working in the first example where an object has a property and method both with the same name? Is this a problem?
Note, I have alternate ways of doing this too. I can otherwise simply give one of them a different name:
onDown(el) {
this._onUp = this.onUp.bind(this, el);
document.addEventListener('mouseup', this._onUp);
}
onUp(el) {
document.removeEventListener('mouseup', this._onUp);
console.log("Mouse down on " + el.id + " and then mouse up.");
}
Or, the { once: true } option works for the example, but not in the actual code I am writing that uses different events (this is just a simplified example):
onDown(el) {
document.addEventListener('mouseup', function () { this.onUp(el) }.bind(this), { once: true });
}
onUp(el) {
console.log("Mouse down on " + el.id + " and then mouse up.");
}
Adding an image that shows the output properties of one of the TestClass objects. It first lists the property values. Since my example only had 1 property, I added another (named anotherProperty) to show the collection of properties, for control purposes. And under Prototype, it lists all the methods in the object. Both sections have an entry named onUp.
And here is another image if the output of the other Test object I built expecting to fail. In here you can see it is built with the same design; it has a property named sameName (which can be referenced) and it has a method under Prototype also names sameName (which fails when calling).
This failure is as expected. But again, in my first example, there is both a property and a method, both with the same name, onUp, and the method does function.
A method is just a property with a function for its value.
A property is a named “thing” attached to an object.
When you create an instance of a class in JavaScript (i.e. call a function with the new keyword) it creates an object with a prototype which is a reference to the class.
When you try to access a property on an object, it first checks the object itself to see if it has such a property. If it can’t find it, it looks at the prototype and checks that object for a property with that name. Then it checks the prototype of that object and so on until it runs out of objects on the prototype chain.
This is how inheritance works in JavaScript.
———
So when you call new TestClass you create an object which has a prototype which has an onUp method.
When the mousedown event fires, you say:
this.onUp = this.onUp.bind(this, id);
Which:
Looks for onUp on the object and doesn’t find it
Looks up the prototype chain
Finds it on the call
Calls bind on it to create a new function
Copies that function to the onUp property of the object itself (creating it)
Now you have an onUp property on the object itself and on the object up the prototype chain.
This generally isn’t a problem but in your specific case, it creates an inefficiency.
The second time you trigger the mousedown event it finds onUp on the object itself, but still calls bind on it to create a new function (which calls the previous function which calls the previous function).
Every time there is another mousedown event you get an additional layer of binding.
You could avoid this by testing with the hasOwnProperty method (which will tell you if the value exists on the object itself and not up the prototype chain).

JavaScript OOP: use private member jQuery again or reselect

Let's look on this JavaScript code:
Person: function() {
var element = null;
this.init = function() {
// select an HTML element created+ added to DOM in init()
element = $("#justCreatedElement");
};
this.workingFunction = function() {
// use private member `element`
element;
// create new variable and select again with jquery
var element = $("#justCreatedElement");
}
}
We can assume that the init() method gets called before any other method.
I have the following Question:
1 In the init() I use JQuery to assign a HTML element to the private member element. Because element is visible for all methods I can just use it again in the method workingFunction2(). What is better coding standard:
I Using element private member in all member mehtods and even maybe write it in any member methof.
II Using element private member only to read in all member methods, except once, where it is set for the first time, here in init(). And if private member element must be changed use a mehtod called changeELement() but do not write it in a method where the user cannot know that it get written to.
III General avoid to use a member variable that holds JQuery selections and always select again in every mehtod
Or can somebody give me his own standards regarding this problem.

Create a local unique variable inside a javascript function that is associated with multiple objects

Is there a way to create a variable that is specific to a function instance but is unique among all the objects created. The best way to explain is through an example.
Let's say I have a loop which iterates through a number of objects. Within that loop I create a new object and assign an annonymous function to a property of the object. The function is tied to a callback handler in the object I created. Within that callback function I want to assign a unique index so that I can access a global array object.
var index=0;
var bar = new Array;
while(true)
{
foo = getObject(); //this is a factory method
foo.eventhandler = function()
{
var internalIndex = index;
bar[internalIndex].readline;
//do some things with internal index when called
}
bar[index]=foo;
index++;
}
The reason this is required is that I have to wait for a number of responses to happen which will fire the eventhandler when completed. Once these are complete I need to work with the data that will be contained within the specific foo object. The event handler tells me nothing about the parent object.
I am working with the PhantomJS framework and the event handler is this particular method
http://phantomjs.org/api/webpage/handler/on-load-finished.html
Any insight or ideas would be deeply appreciated.

Javascript: How to get the right instance of an object from an event handler when their are multiple instances of the object

I created an object and it works great when I have one instance of the object. This isn't the actual Object, but the concept is the same.
function foo(name){
this.name = name;
}
foo.prototype.CreateElement = function(){
var newElement = document.createElement("a");
that = this;
newElement.onclick = function(){
that.SayName(this);
};
document.getElementsByName("body")[0].appendChild(newElement);
}
foo.prototype.SayName = function(LinkObj){
alert(this.name);
LinkObj.href = "http://google.com";
}
I was told that by saving the instance of this to a variable (in this case that), I would still get a reference to the correct object instance for the SayName method. This would leave this to represent the instance of the element passed in from the onclick. This all seems to work properly with only one instance, but when I create multiple instances of the object I end up with the last instance being the instance supplied in the onclick event.
I initialize two objects like this
var ObjOne = new foo("Ted");
ObjOne.CreateElement();
var ObjTwo = new foo("Steve");
ObjTwo.CreateElement();
and when the onclick fires for ObjOne, I get the object instance for ObjTwo and so this.Name is set to Steve.
Does anyone know how I can get the right object instance when the onclick event fires from various instances? I assume i need to bind the correct instance with call, but i'm not really sure how to.
Thanks in advance and hope this wasn't to confusing.
The problem is this line:
that = this;
You declared that without using var, which means it is global, i.e., all your instances are updating the same global that variable. Do this instead:
var that = this;
Then that will be local to each. (That is, local to each closure of the CreateElement function.)
Demo: http://jsfiddle.net/SEsLJ/
Also, document.getElementsByName should be document.getElementsByTagName (assuming you didn't actually give the body a name attribute equal to "body").

How do I reference an object method in javascript from outside the object (through event)?

I am creating a fairly complex object and want to be able to manipulate the object from the outside. I want to reference an privileged method from a click event
function myObject(){
//How do I reference this function?
this.doSomething = function (action){
var doing = action;
}
}
I understand that I could reference the method if I create the object inside a variable like:
var obj = new myObject();
obj.doSomething('hit keyboard');
But the links I am creating to trigger this event are being created by the object, but placed outside the object, so they will not know the container variable to reference it.
i.e.
Do Something
doesn't work because of course the doSomething method is part of the object, not a global function. I could rewrite the code to make it global, but would rather avoid doing that.
If this question doesn't make sense I would be happy to make it clearer. Thanks in advance.
Although it will sit in the global scope, and generally not recommended, you could do this (if you just need to get it working):
function myObject(){
//How do I reference this function?
window.doSomething = function (action){ // USE window instead of this !!
var doing = action;
}
}
By using window instead of this, it can be called in your handler.
IF the object instance is already created, then you can say:
Do Something
Otherwise,
Do Something
You need the object to be in the global scope. Try this:
window.myObject = {
doSomething: function(action) {
alert("Done!");
}
};
and then you make a click event like
Do Something
Example
To define the problem, you're in a particular object's click handler and you want to call a method on another object in your code that you can't get to directly from the object you're in. You really only have these options and you can just pick the one that seems best or seems the least objectionable:
You can make the object that you want to call a method on be in the global scope.
You can make the object that you want to call a method on be findable in the global scope (either in a parent hierarchy or with some sort of global find). It can be done this way without necessarily adding any more top level objects to the global scope.
You can set the object that you want to call a method as instance data on all objects that will have the click handler so that you can just reference the global object from the click handler object's instance data.
You can declare the click handler to have a parameter that contains your object by using function closures and avoid all global data.
The problem is that there's a disconnect between the instance of an object and the element on the page whose event should trigger the object's function. There are various ways to handle that without exposing anything globally. One way, for example, would be to tie the two via an object's property:
Give the element an ID:
<a id="myLink">Do Something</a>
Add element property to object:
function myObject(element){
this.element = element;
if (this.element) {
this.element.onclick = this.doSomething;
}
this.doSomething = function () {
// do stuffs.
}
}
Instantiate object:
var obj = new myObject(document.getElementById('myLink'));
Or simplify the object definition a bit more:
function myObject(element){
this.element = element;
if (this.element) {
this.element.onclick = function () {
// do stuffs.
}
}
}

Categories

Resources