DHTML Jigsaw Puzzle: Cross-Browser; Dragging | WebReference

DHTML Jigsaw Puzzle: Cross-Browser; Dragging

Logo

The DHTML Lab Jigsaw Puzzle, Part IV: Cross-Browser
dragging the puzzle and puzzle pieces


The drag code combines the two browser-specific drag scripts in the same way that we created the generic cross-browser drag code in column 7.

We have, however, made the appropriate modifications to account for the puzzle being dragged only by grabbing the image. For Explorer, we have inserted the statement:

    if (whichEl == elControls) {whichEl = null; return true};
and for Navigator:
    if (imageSelected) whichEl = elPuzzle;

We have made elPuzzle, which includes elControls, draggable. When we mousedown on the control panel, the two browsers handle the event in opposite ways. First of all, our elControls mousedown could be over a button, or just over an unused space in elControls. If we mousedown over a button, Explorer, working from the bottom-up, gives the event to the button. Since the button can respond to a mousedown, it handles the event. If we mousedown on the unused space, Explorer gives the event to elControls, which is not draggable, so our function gives it to elPuzzle, its parent, which is draggable, so we can drag the puzzle.

Navigator, on the other hand, working from the top-down, gives it to the document, and our function identifies elPuzzle as the draggable layer receiving the mousedown. The buttons never get a chance to handle the event. We could, as we did last column, identify the mouse position and return, but this leads to those "sticky" buttons. For Navigator, then, as shown earlier, we track the mouse entering and leaving the image before capturing events. If we are over the image, imageSelected is true, and if we mousedown on the image, we intend to drag elPuzzle, so whichEl becomes elPuzzle. If we are not over the image, grabEl() is called by Navigator only when the puzzle is broken, so grabEl() proceeds to identify the puzzle piece for dragging.

We are not praising one event model at the expense of the other. Both have their benefits, depending on the problem at hand. In our case, the Explorer model is more suitable. To make life simpler, we decide that the puzzle can only be dragged if the mousedown occurs over the image.

Confused? Don't be. Check out Doc JavaScript's two-part in-depth look the browser event models.

currentX = currentY = 0;
whichEl = null;
    
function grabEl(e) {
    if (IE4) {
        whichEl = event.srcElement;
        while (!whichEl.draggable) {
            if (whichEl == elControls) {whichEl = null; return true};
            whichEl = whichEl.parentElement;
            if (whichEl == null) {return true}
        }
    }    
    else { 
        mouseX = e.pageX;
        mouseY = e.pageY;
        
        if (imageSelected) whichEl = elPuzzle;
        if (isBroken) {
            for (i=0; i<document.layers.length; i++) {
                tempLayer = document.layers[i];
                if (!tempLayer.draggable) { continue }
                if ( (mouseX > tempLayer.left+tempLayer.clip.left) && 
                 (mouseX < ((tempLayer.left+tempLayer.clip.left) + tempLayer.clip.width)) && 
                 (mouseY > tempLayer.top+tempLayer.clip.top) && 
                 (mouseY < ((tempLayer.top+tempLayer.clip.top)+ tempLayer.clip.height)) ) {
                      whichEl = tempLayer;
                }
            }
        }
        if (whichEl == null) {return true}
    }
    
    if (whichEl != elPuzzle &&  whichEl != activeEl) {
        if (IE4) {whichEl.style.zIndex = activeEl.style.zIndex + 1}
            else {whichEl.moveAbove(activeEl)};
        activeEl = whichEl;
    }
    
    if (IE4) {
        whichEl.style.pixelLeft = whichEl.offsetLeft;
        whichEl.style.pixelTop = whichEl.offsetTop;
    
        currentX = (event.clientX + document.body.scrollLeft);
        currentY = (event.clientY + document.body.scrollTop); 
    }
    else {
        currentX = e.pageX;
        currentY = e.pageY;
 
        document.captureEvents(Event.MOUSEMOVE);
        document.onmousemove = moveEl;
    }
    return false;
}
    
function moveEl(e) {
    if (whichEl == null) {return true};
    if (IE4) {
        newX = (event.clientX + document.body.scrollLeft);
        newY = (event.clientY + document.body.scrollTop);
    }
    else {
        newX = e.pageX;
        newY = e.pageY;
    }
    distanceX = (newX - currentX);
    distanceY = (newY - currentY);
    currentX = newX;
    currentY = newY;
    if (IE4) {   
        whichEl.style.pixelLeft += distanceX;
        whichEl.style.pixelTop += distanceY;
        if (whichEl.style.pixelLeft + whichEl.clipLeft 
    }
    else {
        whichEl.moveBy(distanceX,distanceY);
        if (whichEl.left + whichEl.clipLeft 
    }
    return false;
}
   
function checkEl() {
    if (whichEl != null) {return false}
}
function dropEl(e) {
    if (whichEl == null) {return true}
    if (NS4) {document.releaseEvents(Event.MOUSEMOVE)}
    if (whichEl == elPuzzle) { whichEl = null; return false}
    if (IE4) {
        dropLeft = event.clientX + document.body.scrollLeft;
        dropTop = event.clientY + document.body.scrollTop;
    }
    else {
        dropLeft = e.pageX;
        dropTop = e.pageY;
    }
    allowLeft = puzzLeft;
    allowRight = puzzLeft + puzzWidth;
    allowTop = puzzTop;
    allowBot = puzzTop + puzzHeight;
    if (dropLeft >= allowLeft && dropLeft = allowTop && dropTop 
            diffLeft = puzzLeft - whichEl.style.pixelLeft;
            diffTop = puzzTop - whichEl.style.pixelTop;
        }
        else {
            diffLeft = puzzLeft - whichEl.left;
            diffTop = puzzTop - whichEl.top;
        }
        whereL = parseInt(diffLeft / pieceWidth) * pieceWidth;
        if(isNaN(whereL)) {whereL = 0}
        modL = parseInt(diffLeft % pieceWidth);
        whereT = parseInt(diffTop / pieceHeight) * pieceHeight;
        if(isNaN(whereT)) {whereT = 0}
        modT = parseInt(diffTop % pieceHeight);
        if (Math.abs(modL) > pieceWidth/2) {
            if (modL > 0) {whereL += pieceWidth} else {whereL -= pieceWidth}
        }
        if (Math.abs(modT) > pieceHeight/2) {
            if (modT > 0) {whereT += pieceHeight} else {whereT -= pieceHeight}
        }
        if (IE4) {
            whichEl.style.pixelLeft = (puzzLeft - whereL);
            whichEl.style.pixelTop = puzzTop - whereT;
        }
        else { 
            whichEl.left = (puzzLeft - whereL);
            whichEl.top = (puzzTop - whereT);
        }
        putL = (NS4) ? whichEl.left : whichEl.style.pixelLeft;
        putT = (NS4) ? whichEl.top : whichEl.style.pixelTop;
        if (putL == puzzLeft && putT == puzzTop) {
            tempEl = whichEl;
            flashTimer = setInterval("visToggle(false)",100); 
        }
    }
    whichEl = null;
    return false;
}

Event Capturing

Since Navigator now captures the mousedown event conditionally, and the mousemove is captured after a mousedown, the only overlap with Explorer is in the capture of the mouseup.

    if (NS4) { document.captureEvents(Event.MOUSEUP); } else { document.onmousedown = grabEl; document.onmousemove = moveEl; document.onselectstart = checkEl; } document.onmouseup = dropEl;

At Last, It's Over...But Is It?

That's our look at the cross-browser version of our popular low-bandwidth customizable jigsaw puzzle. The next three pages repeat the code for quick cut-and-paste. Soon, as a coda to this series, we will add a couple of super features to the puzzle, before packing it in.


Produced by Peter Belesis and

All Rights Reserved. Legal Notices.
Created: Dec. 17, 1997
Revised: Jan. 18, 1998

URL: http://www.webreference.com/dhtml/column11/puzzCBdrag.html