ASP.NET 2.0 AJAX, Safari and Scroll Offset Tricks


Couple days ago, I faced odd bug with DragDropManager at Microsoft.Web.Preview.dll. It doesn’t work correctly if the page is scrolled down in Chrome and Safari browsers. Google did not help. Debugging did not help as well (DragDropManager file is really huge). The last way was to use reflector tool to dig into sources.

The first odd thing was classes hierarchy. Two classes were the most interesting to me: Sys.Preview.UI.IEDragDropManager and Sys.Preview.UI.GenericDragDropManager.

And the following declarations:


Sys.Preview.UI.IEDragDropManager.registerClass('Sys.Preview.UI.IEDragDropManager', Sys.Component);

Sys.Preview.UI.GenericDragDropManager.registerClass('Sys.Preview.UI.GenericDragDropManager', Sys.Preview.UI.IEDragDropManager);

The fuzzy thing is that GenericDragDropManager derives IEDragDropManager . It sounds like abstract class derives from concrete class. I truly believe such naming obscures readability. Gutting the code, I figured out that getScrollOffset method is overridden in for Safari browser (and it means for Chrome as well).


if (Sys.Browser.agent === Sys.Browser.Safari) {
    Sys.Preview.UI.GenericDragDropManager.__loadSafariCompatLayer =
        function Sys$Preview$UI$GenericDragDropManager$__loadSafariCompatLayer(ddm) {
            ddm._getScrollOffset = ddm.getScrollOffset;

            ddm.getScrollOffset = function ddm$getScrollOffset(element, recursive) {
                return { x: 0, y: 0 };

Sounds like code owners just didn’t care about scroll offset. What to do? All what I needed to do is just to prohibit override for Safari to allow base class IEDragDropManager to handle the scroll offset for Safari.


Sys.Preview.UI.IEDragDropManager.prototype.getScrollOffset = function () {

        var left = element.scrollLeft;
        var top = element.scrollTop;
        if (recursive) {
            var parent = element.parentNode;
            while (parent != null && parent.scrollLeft != null) {
                left += parent.scrollLeft;
                top += parent.scrollTop;
                                if (parent == document.body && (left != 0 && top != 0))
                    break;
                parent = parent.parentNode;
            }
        }
        return { x: left, y: top };
}

The hack:


Sys.Preview.UI.DragDropManager._getInstance().getScrollOffset = Sys.Preview.UI.IEDragDropManager.prototype.getScrollOffset

Another annoying issue is auto scroll to the top during drag operation. The hack is as trivial as just an empty handler:


Sys.Preview.UI.DragDropManager._getInstance()._autoScroll = function() { }

The full hack for both issues looks quite strange:


if (Sys.Browser.agent === Sys.Browser.Safari) {
 Sys.Preview.UI.DragDropManager._getInstance().getScrollOffset =
                Sys.Preview.UI.IEDragDropManager.prototype.getScrollOffset
 Sys.Preview.UI.DragDropManager._getInstance()._autoScroll = function() { }
}

The only excuse to ASP.NET Ajax development team is that it was RC build…

  • Jens
    I LOVE YOU!!! Thank you so much! I've been trying to implement Raj Kaimal's "The GridView Row Drag Overlay Extender" and it works beautifully in IE7 and 8 and Firefox, but Safari kept making trouble. Because of you I finally got it to work. Thank you!
  • BuddyJ
    I tried to implement this in Ajax Reorderlist without sucess. Chrome never recognize the scrolloffset. The code is nearly identical, I dont see why it shouldnt work. Any ideas?
  • Anonymous
    Nice fix, You should share it with MS developers.
blog comments powered by Disqus