I have a problem positioning an element in certain browsers. I'm using the jQuery autocomplete found here. The div containing autocomplete values should be directly under the text box, and line up perfectly. The code sets the css left property of the div by using the left property generated by $(textbox).offset();
After un-packing the code to try and fix my problem, I get this:
var a = $(textbox).offset();
element.css({
width: typeof e.width == "string" || e.width > 0 ? e.width : $(textbox).width(),
top: a.top + textbox.offsetHeight,
left: a.left
}).show();
This seems like it should work, and it does work in Firefox. It doesn't work in IE8, Chrome. The top position is always correct, but the sometimes the div is too far to the left, or too far to the right.
On different computers (all with Windows XP), it works in IE8... how can this be? I've also tested it on my Mac, OS 10.5. It works in Firefox, but not Safari.
I've disabled plug-ins, changed screen resolutions, re-sized windows... It just inconsistently works in some places sometimes.
Can anyone think of something I'm missing?
UPDATE:
I have re-worked my code to use the autocomplete supplied with jQuery 1.4.2 and jQuery UI 1.8rc3. It is still broken, same problem. Please help!
UPDATE 2:
See this related question. jQuery UI autocomplete breaks because it uses offset. Does anyone have a work around?
Here is the javascript from the UI autocomplete function that trips up:
.css({ top: (offset.top + this.element.height) + 'px', left: offset.left + 'px' })
If it is changed to top: '0px', left: '0px' it works fine, but is obviously positioned in the wrong spot.
I finally figured out what was happening. I had a css rule defining the width of the body:
body {
width: 900px;
}
Once I changed this to width: 100%; and enclosed the entire page in a div of width 900px, it worked as expected.
It looks like IE uses the body element to measure top and left values for offset(), but uses the window edge when to measure top and left distances when positioning an item absolutely.
I hope this answer will save someone else all the time I've wasted on this...
I met a similar question,finally discovered the float property affects relative,making the relative div not stable in Internet Explorer 8 but performs well in firefox.
Related
It's about positioning an image dynamically with transform: translate(x,y). When i try to resolve the current x and y positions of an image by relying on offsetLeft and offsetTop i always get 8px short on X and Y.
In addition to offsetLeft and OffsetTop, the <image> elements have x and y properties which work perfectly fine in Chrome, Safari and Firefox but there is no such properties in IE. After a few hours struggling, the 8px difference has revealed to be originating from the <body> element's 8px margin. So while image.x and image.y are working just fine; image.offsetLeft and image.offsetTop seem to work buggy since they don't take the <body> element's margin and border value into account.
https://bugzilla.mozilla.org/show_bug.cgi?id=255754
Could you please help me with pure JS (no jQuery please) to find a way around this problem? I can not use image.x and image.y due to compatibility and I may not have a control on the body element's margin or border. So let's pass that.
This is the image.offsetLeft & image.offsetTop version. Only at first move you will notice a 8px shift on X and Y. (tiny but disturbing) : https://jsbin.com/fefogasonu/1/edit?js,output (once the JSBin page is loaded please first click the button "Run with JS" once, as it sometimes fails to start properly)
This is the image.x & image.y version.Works like a charm in Chrome, Safari and Firefox but in IE11. (haven't tested with Edge though) : https://jsbin.com/wibubafuxa/1/edit?js,output
This is more about image.x and image.y properties : https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement
I found differencies between browsers how they report computed style dimensions when browser window is zoomed. The JSBIN example is in http://jsbin.com/pilohonevo/2/. The code is as follows:
$(window).resize(function()
{
var width1=$(".class1").css("width");
$(".class1").css("width",width1);
var width2="200px";
$(".class2").css("width",width2);
var width3=$(".class3").css("width");
$("#width1").html(width1);
$("#width2").html(width2);
$("#width3").html(width3);
$("#overflow1").html($(".overflow1")[0].scrollWidth);
$("#overflow2").html($(".overflow2")[0].scrollWidth);
$("#overflow3").html($(".overflow3")[0].scrollWidth);
});
When you zoom to minimum by pressing CMD- few times and then back to 100% by pressing CMD+ few times, in Chrome (Mac Version 38.0.2125.111), you get the following values:
The white DIV 1 reports its width as 203px, although DIV 2 and 3 reports 200px. Also scrollWidth is 203, which is wrong as well. This means that you cannot use getComputedStyle or jQuerys .css() to get dimensions if you are not sure that browser window is not zoomed. And because zooming is not cancelable you can never be sure and you can never trust to those dimensions. I tested also $(elem).scrollLeft() and $(elem).scrollTop() and those are unreliable as well when zoomed.
So a workaround can be to use "raw" values, not "computed" values.
Is there a cross-browser javascript or jQuery method to get something like getUnComputedStyle() which determines dimensions using raw values from stylesheets and/or style attribute, because they are the only ones that are zoom-safe?
Determining zoom level and make corrections based on that is unreliable according to my tests, because there are browser differencies and error levels in different style properties are not consistently related to zoom level.
(Firefox Mac 33.1 and Safari Mac version 7.1 (9537.85.10.17.1) and IE 11 Win and emulated modes down to version 7 report correct values.
Opera Mac 25.0.1614.68, Safari Win 5.1.7 and the above reported Chrome report wrong values.)
I've reproduced this with Chrome 49 and JQuery 1.11, not in FF and not in Internet Explorer.
However, I believe this to be an artifact of the code as well. The only divs that show this problem are div1 and overflow1, which both use the same system of setting the width to the computed width, repeatedly. What happens is that for some zooms the computed value is 201. You set the width to 201, so for some zooms the computed value becomes 202 and so on. I got 204, for example.
In the Chrome debugger, at zoom 67%, the reported width appears as 199.981, but the values available to Javascript are integers. scrollWidth is 199 while clientWidth and offsetWidth are 200. All of the jQuery width methods return 200 (width, innerWidth, outerWidth). At zoom 33%, scrollWidth and jQuery widths all return 201, althought the debugger reported width is still 199.981.
My assertion is that the problem is a bug in Chrome and probably related to rounding.
As described here: Getting the actual, floating-point width of an element you can get the actual floating point value reported by the debugger with .getBoundingClientRect(). If you want to be completely safe, try using that one.
If I understand what you are trying to accomplish correctly (and if I don't please say so and I'll try to improve my answer), and assuming you have already managed to catch the zooming event some how (which is not a given), you could:
Clone the div you are trying to get the CSS styles from;
Append the clone to the dom in an unobtrusive way (ie, a way in which it will not cover any other elements on the document);
Remove it's style attribute (just in case it was set by other scripts or functions);
Get all the styles you need from it;
Finally, remove the clone from the dom when you are done.
This demo works for me, regardless of page zoom.
jQuery(function($) {
function getRawStyles(sel, styles) {
sel = $(sel);
var clone = sel.clone().removeAttr("style").attr("class", "cloneDiv").insertBefore(sel);
$.each(styles, function(index, style) {
console.log( style + ": " + $(clone[0]).css(style) );
});
$(".cloneDiv").remove();
}
$(document).ready(function() {
$("button", this).on("click", function() {
getRawStyles("#myDiv", ["height", "width"]);
});
});
});
#myDiv {
background: grey;
height: 50px;
width: 200px;
}
.cloneDiv {
left: -10000;
opacity: 0;
top: -10000;
position: absolute;
z-index: -1;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="myDiv" style="height:200px; width: 100px"></div>
<br/>
<button>Log Computed Styles</button>
I have made a rediculasly small snippet to make a sub-header stick to the top.
But since , Like I said - I am by no means a js genious or jQuery genious - and actually far from it - I have my doubts about my own coding abilities..
the demo is here : http://jsfiddle.net/obmerk99/VvKq3/1/
The questions :
1 - there seems to be a lot of plugins (and a lot of questions also
in this very site) with much more code than my snippet - what am i
missing ?? What am I doing wrong ?
2 - will this work cross-browser ?
3 .. and this is a small problem, how to avoid the small "jump" that occurs ?
(if you go to the fiddle, and scroll slowly - you will see that the main div "jumps" when the script is evoked ..
I have tried to add another .pad class to the lower divs -
added class : .pad when script evoked.
.pad{padding-top:42px;}
but it does not seems to work right : http://jsfiddle.net/obmerk99/VvKq3/2/
5 .How can I calculate the real position of the div ? when I try
something like this :
var top = jQuery(window).scrollTop();
var div_top = jQuery('#header_stick').offset().top;
if (top > div_top) // height of float header;
it is jumpy ... http://jsfiddle.net/obmerk99/VvKq3/4/
6 any other suggestions are welcome..
The "jumping" occurs because the element was occupying space in the parent element, and when you change its position to fixed it's suddenly not anymore. I don't know the best way to handle it, but one option would be adding a small span (maybe with a single space) just before your #header_stick, with the same height of it, so when it's class is changed there will still be something there to account for the height difference. (Update: your pad solution is probably the best one, once done right; see below)
Your padding solution might also work, provided that: 1) you remember to remove that class when the user scrolls to the top (in your fiddle I see you adding it, but don't see you removing it); 2) You get the height right - I still couldn't look closely to your code, so I don't know where you got wrong. (Edit: the problem was that your .pad class was using the height of the floating header, not the stick header - fixing that and removing the class yielded what I believe to be the correct result)
About the real position of the div, have you tried subtracting the div's offset from the offset of the parent element? This way you'll have its position relative to the parent (pay attention to things like borders, though - I've recently answered another question where details like this mattered).
Update: your problem here seems to be that, when the position is changed to fixed, the offset also varies wildly. I'd suggest calculating the correct height, once, then storing it somewhere so the scroll function can use it. In other words, don't calculate it while scrolling, that makes it much more difficult to find the right theshold to do the class switch.
Other than that, I think you're code is fine, and I believe it will work cross browsers too (at least standards compliant ones; can't say anything about old versions of IE). Very insightful too, I always wondered how this "trick" worked, now I see it's simpler than I imagined...
You can try this way. I made a shorter version for easier analysis.
fiddle here
<div id="ontop">floating heading</div>
<header>sticky heading</header>
<div id="wrapper">
1<br/>2<br/>3<br/>4<br/>5<br/>6<br/>7<br/>8<br/>9<br/>10<br/>
</div>
#ontop {width:100%; height:80px; background-color:yellow;}
header {width:100%; height:20px; background-color:lightgrey; }
#wrapper {background-color:lightblue; height:5000px;}
.navfixed {position: fixed; top: 0px; z-index: 100; width:100%; display:block; margin-bottom:120px; }
.wrapperBelow{margin-top:22px;}
$(function () {
var elem = $('header'),
wrapperElem = $('#wrapper'),
elemTop = elem.offset().top;
$(window).scroll(function () {
elem.toggleClass('navfixed', $(window).scrollTop() > elemTop);
wrapperElem.toggleClass('wrapperBelow', $(window).scrollTop() > elemTop);
}).scroll();
});
I am trying to position an span element (let us call it "the tooltip span") relative to a input field. To do this, I am wrapping the tooltip span and the input field in another span element (let's call it "the wrapper span") that has position: relative. Then I set position: absolute on tooltip span. This makes the tooltip span position itself relative to the wrapper span but not being part of the page flow - not taking up any space. This is exactly what I want.
Then, using javascript, I set the position of the tooltip relative to the position of the input element. Since the input element can be shaped differently on different pages (script should be globablly applicable), I am using its offsetTop and offsetLeft properties to calculate its position relative to the wrapper span.
However, I am noticing inconsistencies between browsers here. In Firefox, IE6, 7, 8, it works as expected. But in Chrome and Safari the reported offsetTop seems, well, incorrect.
To prove this, I created the test page below:
<html>
<head>
<style type="text/css">
span { font-size: 8px; position: relative; top: 0; left: 0; border: 1px solid red }
</style>
</head>
<body>
<span id="wrapper">
<input id="foo" name="foo" type="text">
</span>
<script type="text/javascript">
document.write("<br>Offset parent: " + document.getElementById("foo").offsetParent.id);
document.write("<br>Offset top: " + document.getElementById("foo").offsetTop);
</script>
</body>
</html>
and loaded it in Firefox and Chrome. Both browser report the wrapper span as its offsetParent, but for Firefox the offsetTop is -8 and for Chrome it is 2. Visually the page renders the same in both browsers.
This gives me a headache, because I cannot just hack in a different offset that I always apply when someone is using Chrome, because if I change the font size, the offsetTop will not change, and my script will break.
Is this a bug? Can I solve this differently?
You Can try using
$(window).load
instead of
$(document).ready
because Explorer and Chrome sets proper offsets only after images have been fully loaded.
I've been having the same problem as you and I realized that (in my case) the thing that was messing up the offset().top value in chrome, was having one or more images without the "height" attribute above the element.
Before
<img src="/images/foo.jpg" />
offset.top() was 100 in Chrome
offset.top() was 150 in Firefox and IE7 (beleive or not, it worked just fine in IE!)
After
<img src="/images/foo.jpg" height="50" width="50" />
offset.top() is 150 in both Firefox, IE7, AND CHROME.
Notice the the difference was 50px, which is actually the same as the image height.
Since I was developing a JQuery plugin, i tryed to collect all the images that had their width and height attributes undefined, and manually setting their size with .width() and .height(), but it didn't work, because Chrome returned 0 for both functions. So, i guess the problem with offset.top() actually relies on that. If JQuery is triying to get te offset.top() value by accumulating the "above" element's height, and one of those elements it's an image with no specified height, "0" will be added to that sum, and therefore the top value will be missing those "ignored" heights.
PS: Sorry for my english, it's been a long time since i wrote such a long text in this language!
Use jQuery. DOM differences between browsers is one of the things it excels at.
Put you code into a window.onload function. I recall having issues when attempting to work with the dom directly from a <script> during page load in firefox, and webkit tends to be slightly more willing to give a sane DOM at such points.
This is just based on prior issues i've encountered, i'm not sure if it's applicable to your case.
I ran into the same problem, and jQuery's position() function was reporting the same as the offset() function. Ultimately it turns out that even waiting for the document to be ready didn't work for me. I had to check offset() later in the flow (in my case, in my handler that is fired on a window.scroll event).
When I try this test code below, on page load, I get different figures for Firefox + Chrome. Once it loads, however, I can press 'd' and I get the same figure for both browsers.
// this produced different results on Chrome + Firefox (Chrome was wrong!)
$(document).ready(function () {
var x = $('#some-div-on-your-page').position().top;
alert("On load, offset is "+x); // Chrome + Firefox report diff figures
$(window).keydown(function(e, r) {
k = e ? e.keyCode : event.keyCode;
if(k == 68) { // press 'd'
var x = $('#some-div-on-your-page').position().top;
alert("Now the offset is "+x); // ...but this is consistent
}
});
}
Hope this helps.
I was experiencing the same problem, and tried adding to my function
$(document).ready(function(){});
and it worked in both Chrome and Firefox
If you get "0" in chrome, see if you are targeting an empty element like "a". It needs to wrap something in order to return the correct offset.
It could be related to the different border/margin values for the HTML and body elements that browsers set by default.
Ahoy!
I've built a little script to check the size of the left-hand margin on page load, resize a div there to fill it, and change the header div to float next to it.
Here's the code:
function buildHeader() {
var containerMarginLeft = $(".container_16:not(:first)").css("margin-left");
var headerHeight = $("#header").height();
$("#stripe").width(containerMarginLeft).height(headerHeight).css("float", "left");
$(".container_16:first").css("float", "left");
$("#header").css("margin-left", 0).width(950);
}
$(document).ready(function(){
// Manipulate layout for the first time
buildHeader();
// Manipulate layout when window is resized
var resizeTimer = null;
$(window).bind('resize', function() {
if (resizeTimer) clearTimeout(resizeTimer);
resizeTimer = setTimeout(buildHeader, 100);
});
});
And the demonstration is here: http://robertmay.me.uk/mockups/plane.html (it creates the line that stretches on the left).
Now, it works in webkit browsers. It doesn't work in Mozilla, and I've not even tried it in IE.
Anyone have any ideas as to why it doesn't seem to work in Mozilla? I have a feeling it might have something to do with the CSS.
$(".container_16:not(:first)").css("margin-left");
This line gives a result of '0px' in Firefox regardless of how wide the window gets. However, Firebug Lite in Safari shows this value as changing depending on the width of the window.
The problem seems to be with the .css('margin-left') part of the statement, since $(".container_16:not(:first)") returns the same element in both browsers. Indeed, Firebug in Firefox shows the Computed Style for this element as having '0px' for marginLeft and marginRight, but this is non-zero in Safari.
As expected, changing from 'margin-left' to 'marginLeft' makes no difference, nor does accessing the attribute directly, like $(".container_16:not(:first)")[0].style.marginLeft, because Firefox is computing it wrong in the first place.
Sorry I don't have an answer, but hopefully this will lead you in the right direction. For me though I would try to align the layout using just CSS, resorting to JavaScript fixes only as a last resort.