JavaScript - Manipulation HTMLElements inside JS object - javascript

In my current project, I've encountered a somewhat strange behavior (from my point of view) when I'm trying to alter the properties af a html element.
In my code, a have defined a javascript object - This object has a 'this.element' property, containing a html element, that gets passed through the constructor. Within this object, I have a couple of functions. In one of theese functions I'm trying to alter some styling of that object, by doing:
this.element.style.visibility = "hidden";
I get no errors when trying to do this, but the style remains unchanged. After some time, I discovered a workaround:
document.getElementById(this.element.id).style.visibility = "hidden";
Which is basically the same. This works, and I can set the style of my element. Though this workaround works, it requires my elements to have ID. While this is not a problem, my coding could get a whole lot easier, if I could get around this.
I'm testing this with Chrome, which is the same browser we'll use once the project is ready for deployment, so using a different browser is not really an option for me.
I would greatly appreciate if anybody can help me understand/solve this situation :)
- Thanks in advance
EDIT: Some more code.
This example I threw together illustrates what I'm doing. However when I run this on it's own, I can't achieve the behavior I was describing.
I don't know if this is of importance, but in my case the function representing "changeAllStyles" works fine when getting called just after the constructor. All subsequence calls of this function, is due to an invocation of the "onMessage" event, coming from websockets.
var myObjArray = [];
function init(){
//Using jQuery to select all elements containing the "data-whatever" attribute:
var elements = $('*[data-whatever]');
//Create a myObj object for each of theese elements:
for (var i = 0; i < elements.length; i++) {
var params = elements[i].getAttribute("data-whatever");
myObjArray.push(new myObj(elements[i], params));
myObjArray[i].changeStyle();
}
}
function myObj(element, params){
this.element = element;
this.params = params;
this.changeStyle = function(){
this.element.style.visibility = "hidden";
};
}
function changeAllStyles(){
for (var i = 0; i < myObjArray.length; i++) {
myObjArray[i].changeStyle();
}
}

It sounds as though elsewhere in the code you're removing the DOM element after having initialized this.element and then recreating it, like this:
HTML:
<div id='bar'><span id='foo'>This is foo</span> inside 'bar'</div>
JavaScript:
var foo = document.getElementById('foo');
var bar = document.getElementById('bar');
bar.innerHTML = "<span id='foo'>This is a new foo</span> inside 'bar'";
foo.style.visibility = "hidden"; // <== No effect, wrong element
As you can see, we're getting the "foo" element, and getting the "bar" element, and then replacing the contents of "bar" with brand-new stuff. That means all of the elements inside "bar" are removed, and then new elements are created. It happens that one of the new elements has the ID "foo" and so
document.getElementById(foo.id).style.visibility = "hidden";
...would work even though foo.style.visibility = "hidden"; doesn't work, because it's a completely different element that happens to have the same ID.

There is nothing wrong with the code you show, except its complexity maybe.
My gut feeling is that unholy things are occuring behind the scene. I cannot debug the code I can't see, but I can propose a cheap workaround.
You could as well collect the elements with document.querySelectorAll('[data-whatever]'), get back your individual parameters with getAttribute() and fiddle with style directly, like so :
function changeAllStyles()
{
var elements = document.querySelectorAll ('[data-whatever]');
for (var i = 0; i < elements.length; i++)
{
var elt = elements[i];
my_parameters = elt.getAttribute ('data-whatever');
play_with (my_parameters);
elt.style.color = a_nice_shade_of_pinkish_brown;
}
}
I concur with T.J. Crowder.
My bet is, someone is destroying and recreating the DOM elements behind your back. If they are recreated by copy, you can still access their clones by Id or class or attributes, but the references to the original DOM elements are dead.
To track this down, you could try something like:
this.changeStyle = function()
{
this.element.__check = "booh!";
var possible_clone = document.getElementById (this.element.id);
if (possible_clone.__check != "booh!")
{
report_to_boss ("something fishy there");
}
};

Related

html + js: dataset variable being ovewriten after assigning it a value based on src attribute

So I have a for loop that iterates over a list of iframes:
var iFr;
for (var i = 0; i < iFrames.length; i++) {
iFr = iFrames[i];
if (isFooBar()) {
iFr.dataset['sourceBackup'] = iFr.src; //assign src value to data-source-backup
iFr.removeAttribute('src'); // remove src attribute
}
}
The weird part is that it seems to remove the src value also from dataset['sourceBackup'] or data-source-backup which I don't understand why. As I'm doing it AFTER assigning it to dataset['sourceBackup'].
UPDATE:
I even tried using object.assign() :
iFr.dataset['sourceBackup'] = Object.assign({}, {'src': iFr.src}).src;
Yet still the iFr.dataset['sourceBackup'] dataset gets erased for some iframes elements but not for others which is confusing.
Update 2
The problem was with outer code not with the code here. I was having multiple references to the same iframe in different contexts. So this was causing the weird behavior.
This is happening because both iFr.dataset['sourceBackup'] and iFr.src are pointing to the same object. The assignment does NOT make a copy of the object. Therefore, when you remove the object, it is not available regardless of which reference you use.
To actually clone the object, see this answer: How do I correctly clone a JavaScript object?

Null check for every variable that invokes a function?

I have a single javascript where I have declared all my variables in the
$(document).ready(function(){
//variables
});
The values of these variables are initialized as well and mostly they are HTML elements. The elements are determined using the ids via document.GetElementById(). Some of these elements exists only in a different page which is not loaded in the browser yet. This results in null error when the variables holding the elements are used for a different purpose.
var container_element = document.getElementById('unique-id');
var count = container_element.getElementsByTagName("div").length;
Since the element with "unique-id" is present in another page which is not loaded in the browser, the second line would return an error because container_element is null. To fix this, I changed the code to
var container_element = document.getElementById('unique-id');
if(container_element) {
var count = container_element.getElementsByTagName("div").length;
}
Is this is the only way to handle such a thing? Should I have to do a null check for every function that I invoke via a variable or is there any other solution or standard / best practice?
You need a guard like that any time the element may or may not exist as of when you use getElementById. You can use the if you've shown, or
var container_element = document.getElementById('unique-id');
var count = !container_element ? 0 : container_element.getElementsByTagName("div").length;
or similar.
Another option is to react to the exception:
var container_element = document.getElementById('unique-id');
var count;
try {
count = container_element.getElementsByTagName("div").length;
} catch (e) {
count = 0;
}
I notice you're using jQuery but not using it in that code. Which is too bad, because if you were, jQuery's set-based nature would mean you didn't need the guard:
var container_element = $('#unique-id');
var count = container_element.find("div").length;
Even though container_element is an empty set, you can call methods on it. Most jQuery methods provide intelligent handling of empty sets. For instance, using find on an empty set returns a (new) empty set, as above.
You still have the option to know whether the element exists (more in this question's answers):
if (container_element[0])
// or
if (container_element.length)

Get elements that have not been added to the DOM

If I create a new HTML element using document.createElement, but I do not add it to the DOM, is there a way to find it? As this is a method of document, it seems somewhat logical that it might become property of document.
Example:
(function(){
document.createElement("a");
})();
console.log(document.getElementsByTagName("a").length); // -> 0
I understand why the above example doesn't work. Is there an alternative?
You're just creating some elements, without insterting them in the DOM.
You need to store them if you want to know how many they are.
var myElements = []:
var elem = document.createElement("a");
myElements.push(elem);
myElements.length; // use their length for your needs
If you are creating elements in several different functions then you need to update the global array:
var linkElements = []; //declared in global
function addLinkElements()
{
var link = document.createElement("a");
// add link element to global array
linkElements.push(link);
// total link elements
alert( "Total anchors: " + linkElements.length + document.getElementsByTagName("a") );
}
Note: Remember to remove update the array in all functions adding these link elements.
hope that helps.

Javascript class initialization and jQuery DOM in memory

Which is the best way between:
var myClass = function() {
this.myContainer = function(){ return $(".container");}
this.mySubContainer = function(){ return this.myContainer().find(".sub"); }
}
AND
var myClass = function() {
this.myContainer = $(".container");
this.mySubContainer = this.myContainer.find(".sub");
}
Is there any concrete differences?
The memory problem arose when I have seen that my web page, that has enough javascript ( about 150KB of mine + libs ) takes more then 300-400MB of RAM. I'm trying to find out the problem and I don't know if this could be one of them.
function myClass{
this.myContainer = function(){ return $(".container");}
this.mySubContainer = function(){ return this.myContainer().find(".sub"); }
}
Here you will need to call it something like myClassInstance.myContainer() and that means jquery will search for .container element(s) any time you are using that function. Plus, it will create 2 additional closures any time you will create new instance of your class. And that will take some additional memory.
function myClass{
this.myContainer = $(".container");
this.mySubContainer = this.myContainer.find(".sub");
}
Here you will have myContainer pointing to an object which already contains all links to DOM nodes. jQuery will not search DOM any time you use myClassInstance.myContainer
So, second approach is better if you do not want to slow down your app. But first approach could be usefull if your DOM is frequently modified. But I do not beleave you have it modified so frequently that you may need to use second approach.
If there is a single variable you are trying to assign , then the second approach looks cleaner..
Yes they are different.
MODEL 1:
In the first model, myContainer is a function variable.It does not contain the jQuery object.You cannot call any of jQuery's methods on the objet. To actually get the jQuery object you will have to say
var obj = this.myContainer() or this.myContainer.call()
MODEL 2:
The second model stores the actual jQuery object.
try alerting this.myContainer in both models, u will seee the difference.
Yes this is different. after you fix your syntax error :
function myClass(){... // the parentheses
1st
When you create a new object var obj = new myClass(), you are creating two functions, and the jquery object is not returned until you call it
var container = obj.myContainer();
2nd
As soon as the object is initialized the dom is accessed and you have your objects cached for later use;
var container = obj.myContainer;

Javascript notification when modifying array

I got something for the javascript developers amongst us.
I got the following class:
function MyClass(){
this.__defineSetter__("array", function(val){
alert("setter called");
this._array = val;
});
this.__defineGetter__("array", function(){
alert("getter called");
return this._array;
});
this._array = new Array();
};
Now, what happens is that when I execute
var a = new MyClass();
a.array[0] = "MyString";
alert(a.array[0]);
the getter is called twice (which is fine), but the setter is never executed, as the actual array reference does not change, only the content (I guess expected behavior).
However, I'd also need to be "notified" when the array-content is modified. Thus, the call
a.array[0] = "MyString";
should also cause a setter-call (or something similar, important is to receive a notification when the array content has changed.
Anybody into this? How can this be achieved?
As we know,alert(a.array[0]); will only trigger a.array's getter/setter,and a.array[0] equal var p = a.array; p[0] which means what you want is trigger p[0]'s getter/setter,not just p's getter/setter.
So,we can change our mind to this thinking:
add getter/setter to all items of p
so,we can do it like this:
if some like p[6] = 0 is used , which will trigger p's getter/setter , judge if all item of p has getter/setter .if not add it.
if some like p = [2,3,4] is use , simply first set getter/setter to the value.
and the code is: Jsfiddle

Categories