.NET (295) administrative (41) Ajax (42) AngularJS (2) ASP.NET (144) bicycle (2) books (180) browser (8) C# (133) cars (1) chess (28) CodePlex (10) Coma (8) database (47) deployment (3) Entity Framework (2) essay (110) flash/shockwave (2) flex (1) food (3) friend (2) game (20) idea (5) IIS (8) javascript (82) LInQ (2) Linux (6) management (4) manga (42) misc (669) mobile (1) movies (90) MsAccess (1) murder (2) music (64) mysql (1) news (99) permanent (1) personal (68) PHP (1) physics (2) picture (307) places (12) politics (13) programming (501) rant (120) religion (3) science (43) Sharepoint (3) software (58) space (1) T4 (2) technology (11) Test Driven Development (4) translation (2) VB (2) video (97) Visual Studio (44) web design (45) Windows API (8) Windows Forms (3) Windows Server (4) WPF/Silverlight (63) XML (11)

Tuesday, September 16, 2008

offsetParent null in FireFox. Absolute position in FireFox yields 0,0

I've spent about a day on a thing that I can only consider a FireFox bug. As a complete reverse from what I would expect from a javascript script, it worked anywhere but in FireFox! And FireFox 2.1, I haven't even installed 3.0 yet.

It concerned a simple javascript function from a third party that was supposed to get the absolute positioning of an element when clicked. I've written one myself a while ago, but it didn't work either! Here is the function that I was trying to fix:
function getPos(n) {
var t = this, x = 0, y = 0, e, d = t.doc, r;

n = t.get(n);

// Use getBoundingClientRect on IE, Opera has it but it's not perfect
if (n && isIE) {
n = n.getBoundingClientRect();
e = t.boxModel ? d.documentElement : d.body;
x = t.getStyle(t.select('html')[0], 'borderWidth'); // Remove border
x = (x == 'medium' || t.boxModel && !t.isIE6) && 2 || x;
n.top += t.win.self != t.win.top ? 2 : 0; // IE adds some strange extra cord if used in a frameset

return {x : n.left + e.scrollLeft - x, y : n.top + e.scrollTop - x};
}

r = n;
while (r) {
x += r.offsetLeft || 0;
y += r.offsetTop || 0;
r = r.offsetParent;
}

r = n;
while (r) {
// Opera 9.25 bug fix, fixed in 9.50
if (!/^table-row|inline.*/i.test(t.getStyle(r, "display", 1))) {
x -= r.scrollLeft || 0;
y -= r.scrollTop || 0;
}

r = r.parentNode;

if (r == d.body)
break;
}

return {x : x, y : y};
}


As you see, it is a little more complex than my own, although I don't know if it works better or not.

Anyway, I found that the problem was simple enough: the element I was clicking did not have an offsetParent! Here is a forum which discusses a possible cause for it. Apparently the Gecko rendering engine that FireFox uses does not compute offsetParent, offsetTop or offsetLeft until the page has finished loading. I didn't find anything more detailed and there were just a few pages that seemed to report a problem with offsetParent null in FireFox.

I tried to solve it, but in the end I gave up. My only improvement to the script was this line:
while (r&&!r.offsetParent) {
x+=r.offsetLeft||0;
y+=r.offsetTop||0;
r=r.parentNode;
};
which resulted in a more localised position, i.e. the position of the closest parent to which I could calculate a position.

In the end the problem was solved by restructuring the way the dynamic elements on the page were created, but I still couldn't find either an official cause or a way to replicate the issue in a simple, separate project. My guess is that some types of DOM manipulations while the page is loading (in other words, scripts that are just dropped on the page and not loaded in the window 'load' event which change stuff in the page element tree) lead to FireFox forgetting to compute the offset values or just even assuming that the page is never loaded.

2 comments:

livetrackmap said...

Hi !

First of all, forgive me for my english, I improve it every day but the road is long ...

I'm trying to embed some tinyMCE in a modal window using jqModal (with jQuery) , and I have a problem with firefox and safari : the position of the color map is wrong when clicked the police color button. I looked into the code of tinyMCE and I assumed that the code that was incorrect was the part you mentionned in your post.

So I was interested ... did you figure a way to correct the getPos function in that piece of code ?? :).

It would be so great, I hope you're coming around sometimes and you will have something good to answer to my question. In that case, you'll be my hero for the week :).

See you !

JM

Siderite said...

Well, for TinyMCE, if you are using .Net, there is something called TinyMCE.Net, which is a .Net control encapsulating TinyMCE.

As for the offsetParent problem, try to execute the javascript in the onload event or (in .Net) use ScriptManager.RegisterStartupScript instead of putting the code inline.

That means that if you have a script tag with some instructions in it, transform them into a function and execute the function when the page has completed loading.