JQuery offset returns elements “old” position in IE7

 ̄綄美尐妖づ 提交于 2019-12-13 01:34:07

问题


Ok, got a weird bug going on here and so far google has failed to turn up anything addressing it.

When using IE7 (actually IE8 compatibility) the JQuery offset function is not returning the current offset of an element. Here is a really quick test page to demo the issue:

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>

    <script src="Scripts/jquery-1.7.1.js" type="text/javascript"></script>

    <style type="text/css">
        div#Content
        {
            height: 400px;
            overflow: auto;
            border: solid 1px black;
            padding: 2px;
        }
        div.Spacer
        {
            height: 300px;
            border: dotted 1px black;
            background: #ddd;
        }
        div.Wrapper
        {
            padding: 5px;
        }
        div#MoveMe
        {
            position: absolute;
            z-index: 5;
            display: none;
            background-color: Blue;
        }
    </style>
</head>
<body>

    <script type="text/javascript">
        function SetPosition() {
            var divContent = $("div#Content");
            var divMoveMe = $("div#MoveMe");
            var Textbox = $("input#textbox");

            var divMoveMeOffset1 = divMoveMe.offset();

            divMoveMe.css({ "top": "0px", "left": "0px" });
            divMoveMe.height(0);
            divMoveMe.width(0);

            var divMoveMeOffset = divMoveMe.offset();
            var TextboxOffset = Textbox.offset();
            var ContentScrollTop = divContent.scrollTop();
            var ContentScrollLeft = divContent.scrollLeft();
            var divLeft = ((TextboxOffset.left - divMoveMeOffset.left)) + Textbox.outerWidth();
            var divTop = (TextboxOffset.top - divMoveMeOffset.top);
            divMoveMe.css({ "top": divTop + "px", "left": divLeft + "px" });
            divMoveMe.height(Textbox.outerHeight());
            divMoveMe.width(Textbox.outerWidth());

            var divMoveMeOffset2 = divMoveMe.offset();
        }

        $(document).ready(function() {
            $("div#MoveMe").show();
            SetPosition();
            $("div#Content").scroll(SetPosition);
            $(window).scroll(SetPosition);
            $(window).resize(SetPosition);
        });
    </script>

    <div id="Content">
        <div id="Spacer1" class="Spacer">
        </div>
        <div class="Wrapper">
            <input id="textbox" />
            <div id="MoveMe">
            </div>
        </div>
        <div id="Spacer2" class="Spacer">
        </div>
    </div>
    <div id="Spacer3" class="Spacer">
    </div>
</body>
</html>

In IE 8 and firefox the blue div is correctly positioned adjacent to the textbox, and holds this relative position through scrolling etc. However when I kick back to IE7 it is not holding position correctly.

As near as I can tell the offset function is always returning the position values for the div at the time the SetPosition function was called, they do not see the changes that should result from setting the top and left css properties. I have verified this by adding a couple of extra calls to the offset method and putting a watch on the three results. All three report the same offset numbers even though top and left are changed between the calls.
Does anyone know a way to make offset update after changing the css properties? Or an alternate approach?

I should note that this is going into a web control, none of the markup, or related styles, outside of the Wrapper div can be modified for a valid solution, in fact I need to be able to modify them without breaking this behavior.

UPDATE: in playing with this a bit more I noticed that the issue only seems to occur when the function is called during the scroll event, the call during document ready works correctly.


回答1:


Ok, I figgured out what was happening, if not exactly why, and I was able to come up with a work around.

In IE 7, if my method is called from the content div’s scroll event, then the jquery position and offset methods will always return the same values within the context of the event. Even though the element is repositioned multiple times, these methods appear to return the values for the position it held at the start of the event. If the method is called by other means, such as the document ready event, then everything seems to work correctly and the position calculations are sensitive to the changing position of the element. I should also note that falling back and calculating the position using standard javascript has the same behavior.

The workaround I came up with was to split my positioning method into two parts, the first part zeros out the position of my div and the second part sets the new position. I then use setTimeout in the first method to call on the second method. This breaks me out of the event context so that my position calculations are correctly able to find the zero point of the div.

var t;
function SetPosition() {
    var divMoveMe = $("div#MoveMe");
    if (t) {
        clearTimeout(t);
    }

    divMoveMe.css({ "top": "0px", "left": "0px" });
    divMoveMe.height(0);
    divMoveMe.width(0);

    t = setTimeout(function() { SetPosition2(); }, 0);
}
function SetPosition2() {
    var divMoveMe = $("div#MoveMe");
    var Textbox = $("input#textbox");

    var divMoveMeOffset = divMoveMe.offset();
    var TextboxOffset = Textbox.offset();
    var divLeft = ((TextboxOffset.left - divMoveMeOffset.left)) + Textbox.outerWidth();
    var divTop = (TextboxOffset.top - divMoveMeOffset.top);
    divMoveMe.css({ "top": divTop + "px", "left": divLeft + "px" });
    divMoveMe.height(Textbox.outerHeight());
    divMoveMe.width(Textbox.outerWidth());
}

EDIT: This version of the code works correctly as well. It appears that the key thing is to break out of the event context before performing the position calculations.

var t;
function SetPosition() {
    if (t) {
        clearTimeout(t);
    }
    t = setTimeout(function() { SetPosition2(); }, 0);
}
function SetPosition2() {
    var divContent = $("div#Content");
    var divMoveMe = $("div#MoveMe");
    var Textbox = $("input#textbox");

    divMoveMe.css({ "top": "0px", "left": "0px" });
    divMoveMe.height(0);
    divMoveMe.width(0);

    var divMoveMeOffset = divMoveMe.offset();
    var TextboxOffset = Textbox.offset();
    var divLeft = ((TextboxOffset.left - divMoveMeOffset.left)) + Textbox.outerWidth();
    var divTop = (TextboxOffset.top - divMoveMeOffset.top);
    divMoveMe.css({ "top": divTop + "px", "left": divLeft + "px" });
    divMoveMe.height(Textbox.outerHeight());
    divMoveMe.width(Textbox.outerWidth());
}



回答2:


Answer to your Question

jQuerys Offset doesn't returns the distance to the browser window but to the top of the document.

Your approach should be to subtract the $(window).scrollTop() from the $(div).offset().top

Other Advices

Next I think these lines are incorrect code... not entirely sure.

$("div#Content").scroll(SetPosition);
$(window).scroll(SetPosition);
$(window).resize(SetPosition);

Should be SetPosition() instead if SetPosition. But SetPosition can't be used as argument without returning a value?!

By the way you can use:

divMoveMe.css(...).scroll(...).resize(...);

insted of:

divMoveMe.css({ "top": divTop + "px", "left": divLeft + "px" });
divMoveMe.height(Textbox.outerHeight());
divMoveMe.width(Textbox.outerWidth());


来源:https://stackoverflow.com/questions/8594338/jquery-offset-returns-elements-old-position-in-ie7

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!