views:

419

answers:

1

I have an ASP.Net page that contains a <div> with an <img> tag within. The image is large so the <div> is set with a size of 500x500 pixels with overflow set to scroll.

I'm attempting to add image panning using a click and drag on the image. However, every time I begin dragging the image escapes the boundary of the element and consumes the entire page.

Here is example code that illustrates the problem:

<%@ Page Language="VB" AutoEventWireup="false" CodeFile="Default.aspx.vb" Inherits="_Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;

<html xmlns="http://www.w3.org/1999/xhtml"&gt;
    <head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <div id="divInnerDiv" style="overflow:scroll; width:500px; height:500px; cursor:move;">
            <img id="imgTheImage" src="Page0001.jpg" border="0" />
        </div>

        <script type="text/javascript">
            document.onmousemove = mouseMove;
            document.onmouseup   = mouseUp;

            var dragObject  = null;
            var mouseOffset = null;

            function mouseCoords(ev){
                if(ev.pageX || ev.pageY){
                    return {x:ev.pageX, y:ev.pageY};
                }
                return {
                    x:ev.clientX + document.body.scrollLeft - document.body.clientLeft,
                    y:ev.clientY + document.body.scrollTop  - document.body.clientTop
                };
            }

            function getMouseOffset(target, ev){
                ev = ev || window.event;

                var docPos    = getPosition(target);
                var mousePos  = mouseCoords(ev);
                return {x:mousePos.x - docPos.x, y:mousePos.y - docPos.y};
            }

            function getPosition(e){
                var left = 0;
                var top  = 0;

                while (e.offsetParent){
                    left += e.offsetLeft;
                    top  += e.offsetTop;
                    e     = e.offsetParent;
                }

                left += e.offsetLeft;
                top  += e.offsetTop;

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

            function mouseMove(ev) {
                ev           = ev || window.event;
                var mousePos = mouseCoords(ev);

                if(dragObject){
                    dragObject.style.position = 'absolute';
                    dragObject.style.top      = mousePos.y - mouseOffset.y;
                    dragObject.style.left     = mousePos.x - mouseOffset.x;

                    return false;
                }
            }
            function mouseUp(){
                dragObject = null;
            }

            function makeDraggable(item){
                if(!item) return;
                item.onmousedown = function(ev){
                    dragObject  = this;
                    mouseOffset = getMouseOffset(this, ev);
                    return false;
                }
            }

            makeDraggable(document.getElementById("imgTheImage"));
        </script>

    </div>
    </form>
</body>
</html>

Also note that this HTML works fine in a non-ASP.Net page.

Is there something in the ASP.Net Javascript that is causing this issue? Does anyone have a suggestion for panning a JPEG within a <div> block that will work in ASP.Net? I have tried using the jQueryUI library but the result is the same.

+1  A: 

your div "divInnerDiv" must be styled as

positition: relative;

in order for the img with

position: absolute; 

to be positioned from the origin of the div. otherwise, it's absolutely positioned from the origin of the page. i think you can also get away with absolutely positioning your div if you want to place it manually rather than in the flow of the document.

you have some different issues with firefox/chrome, but it's time to go home and this will at least keep your image from popping out of its container as soon as you touch it.

edit: firefox/chrome issues

in the function mouseMove where you are setting the position of the element, firefox and chrome require units with the value. it should look like something this:

dragObject.style.top = (mousePos.y - mouseOffset.y).toString() + 'px';
dragObject.style.left = (mousePos.x - mouseOffset.x).toString() + 'px';

there's also a general issue with how the script determines where to move the element to but i'm not sure what it is right now.

edit2: how to fix the jumpy image

we're going to change the procedures a bit. rather than placing the image based on how far the mouse has moved from the last move event, we're going to see where the mouse is compared to where it was when mousedown fired and move the image that far from where it started.

add a new variable (towards the top) to hold the starting coordinates of the img element

var imgStartLoc = null;

we'll set this in the onmousedown function (maybe not the cleanest way to do this but it works)

imgStartLoc = {
    x: isNaN(parseInt(dragObject.style.left)) ? 0 : parseInt(dragObject.style.left),
    y: isNaN(parseInt(dragObject.style.top)) ? 0 : parseInt(dragObject.style.top)
};

also in the mousedown function replace

mouseOffset = getMouseOffset(this, ev);

with

ev = ev || window.event;
mouseOffset = mouseCoords(ev);

since we want to know where the mouse is on the page rather than in the div.

in the mouseMove function change the top/left assignment lines to

var mouseDelta = { x: mousePos.x - mouseOffset.x, y: mousePos.y - mouseOffset.y }
dragObject.style.top = (imgStartLoc.y + mouseDelta.y).toString() + 'px';
dragObject.style.left = (imgStartLoc.x + mouseDelta.x).toString() + 'px';

all should be well after this.

lincolnk
Awesome! I still struggle with CSS positioning. And this was a perfect example of using some javascript I found on the Internet without really reviewing it. If you have a chance to comment on the Firefox/Chrome issue I'll happily mark this as the accepted answer. Thanks.
Bob Mc
Nice work lincolnk. A well-deserved 10 points for this one.
Bob Mc