Why is this javascript function so slow on Firefox? - javascript

This function was adapted from the website: http://eriwen.com/javascript/measure-ems-for-layout/
function getEmSize(el) {
var tempDiv = document.createElement("div");
tempDiv.style.height = "1em";
el.appendChild(tempDiv);
var emSize = tempDiv.offsetHeight;
el.removeChild(tempDiv);
return emSize;
}
I am running this function as part of another function on window.resize, and it is causing performance problems on Firefox 3.6 that do not exist on current Safari or Chrome. Firefox's profiler says I'm spending the most time in this function and I'm curious as to why that is.
Is there a way to get the em size in javascript without doing all this work? I would like to recalculate the size on resize incase the user has changed it.

Seems like the function could just be
function getEmSize(el) {
return Number(getComputedStyle(el, "").fontSize.match(/(\d+)px/)[1]);
}
In other words, you can just get the computed font size of the element rather than creating a div and sizing it.

Try this (it's the same function with the value stashed so it only runs once):
var getEmSize = function() {
var underlyingFunction = function(el) {
var tempDiv = document.createElement("div");
tempDiv.style.height = "1em";
el.appendChild(tempDiv);
var emSize = tempDiv.offsetHeight;
el.removeChild(tempDiv);
underlyingFunction = function() {
return emSize;
};
return emSize;
};
return function(el) {
return underlyingFunction(el);
};
};

Related

naturalWidth and naturalHeight returns 0 using onload event

I have read countless of answers of this issue and I came up with the following, but it doesn't work either.
function fitToParent(objsParent, tagName) {
var parent, imgs, imgsCant, a, loadImg;
//Select images
parent = document.getElementById(objsParent);
imgs = parent.getElementsByTagName(tagName);
imgsCant = imgs.length;
function scaleImgs(a) {
"use strict";
var w, h, ratioI, wP, hP, ratioP, imgsParent;
//Get image dimensions
w = imgs[a].naturalWidth;
h = imgs[a].naturalHeight;
ratioI = w / h;
//Get parent dimensions
imgsParent = imgs[a].parentNode;
wP = imgsParent.clientWidth;
hP = imgsParent.clientHeight;
ratioP = wP / hP;
//I left this as a test, all this returns 0 and false, and they shouldn't be
console.log(w);
console.log(h);
console.log(ratioI);
console.log(imgs[a].complete);
if (ratioP > ratioI) {
imgs[a].style.width = "100%";
} else {
imgs[a].style.height = "100%";
}
}
//Loop through images and resize them
var imgCache = [];
for (a = 0; a < imgsCant; a += 1) {
imgCache[a] = new Image();
imgCache[a].onload = function () {
scaleImgs(a);
//Another test, this returns empty, for some reason the function fires before aplying a src to imgCache
console.log(imgCache[a].src);
}(a);
imgCache[a].src = imgs[a].getAttribute('src');
}
}
fitToParent("noticias", "img");
To summarise, the problem is the event onload triggers before the images are loaded (or that is how I understand it).
Another things to add:
I don't know at first the dimensions of the parent nor the child,
because they varied depending of their position on the page.
I don't want to use jQuery.
I tried with another function, changing the onload event to
window, and it worked, but it takes a lot of time to resize because
it waits for everything to load, making the page appear slower,
that's how I came to the conclusion the problem has something to do
with the onload event.
EDIT:
I made a fiddle, easier to look at the problem this way
https://jsfiddle.net/whn5cycf/
for some reason the function fires before aplying a src to imgCache
Well, the reason is that you are calling the function immedeatly:
imgCache[a].onload = function () {
}(a);
// ^^^ calls the function
You call the function and assign undefined (the return value of that function) to .onload.
If you want to use an IIFE to capture the current value of a, you have to make it return a function and accept a parameter to which the current value of a is assigned to:
imgCache[a].onload = function (a) {
return function() {
scaleImgs(a);
};
}(a);
Have a look again at JavaScript closure inside loops – simple practical example .

Testing multiple browsers with protractor backed by page objects

I'm writing a test where two browsers need to interact. The problem with simply forking the browser is that my page objects still reference the old browser. I didn't want to rewrite all of my PO's to take the browser as a parameter so I tried the first solution found in the link below where they overwrite the global variables with the new browser's version :
Multiple browsers and the Page Object pattern
However, changing the global variables doesn't seem to work as all the subsequent page object functions that I call are performed against the original browser instance. I have tried logging the window handler before and after the switch and they are indeed different which only baffles me further. Here's some of the code.
spec:
var MultiBrowserFunctions = require('../common/multiBrowserFunctions.js');
var HomePage = require('../home/home.po.js');
describe('blah', function(){
it('blah', function(){
MultiBrowserFunctions.openNewBrowser(true);
HomePage.initializePage();
});
});
MultiBrowserFunctions:
(function() {
var browserRegistry = [];
module.exports = {
openNewBrowser: function(isSameUrl){
if(typeof browserRegistry[0] == 'undefined'){
browserRegistry[0] = {
browser: browser,
element: element,
$: $,
$$: $$,
}
}
var tmp = browser.forkNewDriverInstance(isSameUrl);
var id = browserRegistry.length;
browserRegistry[id] = {
browser: tmp,
element: tmp.element,
$: tmp.$,
$$: tmp.$$,
}
switchToBrowserContext(id);
return id;
},
resetBrowserInstance : function(){
browserRegistry.splice(1,browserRegistry.length);
switchToBrowserContext(0);
}
}
function switchToBrowserContext(id){
console.log('---------------------------switching to browser: ' + id);
browser=browserRegistry[id].browser;
element=browserRegistry[id].element;
$=browserRegistry[id].$;
$$=browserRegistry[id].$$;
}
}());
My questions are:
(1) why doesn't this work?
(2) Is there some other solution that doesn't involve rewriting all of my po's?
What you can do is, save the browsers in different variables and then switch between them by overriding the globals via a utility or something.
describe('Switching browsers back and forth', function () {
var browserA, browserB;
it('Browser Switch', function () {
var browsers = {
a : browser,
b : browser.forkNewDriverInstance(true)
};
browserA = browsers.a;
browserB = browsers.b;
var browserAndElement = switchBrowser(browserB);
browser = browserAndElement.browser;
element = browserAndElement.element;
//do your stuff
var browserAndElement = switchBrowser(browserA);
browser = browserAndElement.browser;
element = browserAndElement.element;
//do your stuff
});
});
The switchBrowser() can look like following:
this.switchBrowser = function (currentBrowser) {
browser = currentBrowser;
element = currentBrowser.element;
return {
browser : browser,
element : element
}
}
In this way you don't have to rewrite your POs to take in the new globals.
Hope it helps!
Cheers

javascript onload function works, onresize does not

I have a function in my header to adjust the size of an iframe and I call it in the footer for both window.onload and window.onresize.
var resize = function(elementId) {
document.getElementById(elementId).height = parseInt(document.getElementById(elementId).offsetWidth)*0.63;
};
onload works:
window.onload = resize("youtube");
onresize does not:
window.onresize = resize("youtube")
Forgive me if I'm doing anything wrong with my javascript. I'm still kind of new to it.
You are executing the function right away instead of returning a function.
So if you log what window.onload contains, it would contain undefined
You need to return a function (also you might want to cache the element, like I did):
var resize = function(elementId) {
return function(){
var element = document.getElementById(elementId);
element.height = parseInt(element.offsetWidth) * 0.63;
};
};
What the above does still allows for you to do:
window.onload = resize("youtube");
Since resize now returns a callback window.onload (in theory) is now the same as:
window.onload = function(){
var element = document.getElementById("youtube");
element.height = parseInt(element.offsetWidth) * 0.63;
};
window.onresize (and window.onload for that matter) should be a function reference;
window.onresize = function () {
resize('youtube')
}
Your implementation will execute the functions and assign the return value to the window properties.

Javascript closures and memory leak risks

Recetly I was looking for memory leaks in my javascript code. Ater finding some major leaks I starter to look for minor and found something that could be a potential leak - the "hoverIntent.js" plugin. I would like to ask if this is really a leak or am I a bit too overzealous?
General schema fo the code (full code here http://cherne.net/brian/resources/jquery.hoverIntent.js):
(function($) {
$.fn.hoverIntent = function(f,g) {
//...
var track = function(ev) {
cX = ev.pageX;
cY = ev.pageY;
};
var compare = function(ev,ob) {
//... function body
};
var delay = function(ev,ob) {
//... function body
};
var handleHover = function(e) {
//... function body
};
return this.bind('mouseenter',handleHover).bind('mouseleave',handleHover);
};
})(jQuery);
I know that many js plugins are written that way, but... If I get that correctly every time I invoke hoverIntent on my object, 3 new functuions (closures) are created? Isn't it a possible memory leak (or at least a performace issue)?
Wouldn't it be better to write is this way:
(function($) {
//create the methods only once on module init?
var track = function(ev) {
cX = ev.pageX;
cY = ev.pageY;
};
var compare = function(ev,ob) {
//... function body
};
var delay = function(ev,ob) {
//... function body
};
var handleHover = function(e) {
//... function body
};
$.fn.hoverIntent = function(f,g) {
//no closures here
return this.bind('mouseenter',handleHover).bind('mouseleave',handleHover);
};
})(jQuery);
You are correct, your second example would use less memory because of less closure functions. But as soon as you event isn't callable (element removed etc.) they would disappear again so it is not a "leak" as the memory isn't lost forever.
Also many plugins use the closure by setting the current state of an element in a variable instead of the element itself.

Javascript document.getElementsByClassName returning undefined

I have a function which should be fairly straightforward and is supposed to be done after loading in order to reduce initial load time.
Basically I am using this code to get all of the elements with class "prefImg" and do some stuff with them. But when debugging in firebug, it says that the var divsList is undefined.
function populatePrefsList()
{
var divsList = new Array();
divsList = document.getElementsByClassName("prefImg");
var x = divsList.length;
var i = 0;
for(i=0; i<divsList.length; i++) {
var imgs = divsList[i].getElementsByTagName("img");
var imgSRC = imgs[0].src;
var alt = imgs[0].alt;
var descs = divsList[i].getElementsByTagName("h4");
var desc = descs[0].innerHTML;
//var thisPref = new preference(imgSRC, alt, desc);
//prefsList[i] = thisPref;
}
}
Obviously I have the breakpoint on var x = divsList.length...
I cannot understand this, I initially had the script in the Head of the page, but figuring it may have not loaded the divs yet, have moved it to the bottom of the Body. This did not solve it.
I have had var divsList = document.getElementsByClassName("prefImg");
If anyone could tell me where I have gone wrong then I would be grateful. There are about 50 divs with the className prefImg.
Cheers
You can use querySelectorAll instead of getElementsByClassName:
change divsList = document.getElementsByClassName("prefImg");
to this divsList = document.querySelectorAll(".prefImg");
DEMO - http://jsfiddle.net/ya3gU/
BTW, you do not need to declare the array divsList before you set it. Just do:
var divsList = document.querySelectorAll(".prefImg");
do not write this code in the head..
because this means the body did not load yet.
do it in the end of your body tag or use-
window.addEventListener("load", function()
{
// code here
});
you can use an eventhandler to the load event of the window object, to run the script only when the window has finished load
function populatePrefsList()
{
var divsList = new Array();
divsList = document.getElementsByClassName("prefImg");
var x = divsList.length;
var i = 0;
for(i=0; i<divsList.length; i++) {
var imgs = divsList[i].getElementsByTagName("img");
var imgSRC = imgs[0].src;
var alt = imgs[0].alt;
var descs = divsList[i].getElementsByTagName("h4");
var desc = descs[0].innerHTML;
//var thisPref = new preference(imgSRC, alt, desc);
//prefsList[i] = thisPref;
}
}
window.onload = function(){
populatePrefsList();
}
Older browsers (like IE6, IE7, IE8) doesn´t support getElementsByClassName and so they returns undefined.
In newer browsers the return value is never undefined. It is
mostly a HTMLCollection (but after W3C spec it should be a NodeList).
https://developer.mozilla.org/en/DOM/document.getElementsByClassName
But I think in your case the real problem is a bug in Firebug:
http://code.google.com/p/fbug/issues/detail?id=5336
It is fixed and a patch is committed and will be part of Firebug 1.10a6
Because it returns a HTMLCollection, so you should add a [number] at the end of the line:
divsList = document.getElementsByClassName("prefImg")[0];
Also it is a good idea to load this function after everything load completely by using:
window.load = function() {
populatePrefsList();
}
This is not supported by all browsers...any browser that does not support it, you would have to implement your own.
function getElementsByClassName(node,classname) {
if (node.getElementsByClassName)
return node.getElementsByClassName(classname);
else {
// custom
}
}

Categories

Resources