Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations bkrike on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Help with drag and drop functionality 1

Status
Not open for further replies.

snotmare

Programmer
Jan 15, 2004
262
US
Greetings mighty java scripters!

I have an HTML application that I'd like to incorporate drag and drop functionality. Since this is the first time I've tried this and I didn't want muck up my app, I've created this test html page to expirament a bit.

Here is the code, and at the bottom are my questions:
Code:
<HTML>
<HEAD>

<STYLE>
TR			{
			height: 200px;
			}
			
TD			{
			text-align: center;
			vertical-align: middle;
			width: 200px;
			}
			
.testTable	{
			left: 0px;
			position: absolute;
			top: 0px;
			}
			
.log		{
			border: 1px solid #000000;
			height: 100px;
			left: 0px;
			overflow: auto;
			position: absolute;
			top: 620px;
			width: 700px;
			}
			
.dragCell	{
			background-color: #555555;
			border: 1px solid #000000;
			height: 200px;
			filter: alpha(opacity=55);
			left: 0px;
			opacity: 0.05;
			position: absolute;
			top: 0px;
			width: 200px;
			Z-Index: 2;
			}
			
</STYLE>
<SCRIPT type=text/javascript>

var clickedCell = null;

function page_onLoad(){
	document.getElementById("dragCell").style.display = "NONE";
	initTable();
}

function generic_onMouseDown(cell){
	document.getElementById("dragCell").style.display = "BLOCK";
	document.getElementById("dragCell").style.left = event.clientX - 100;
	document.getElementById("dragCell").style.top = event.clientY - 100;
	
	cell.style.backgroundColor = "#999999";
	clickedCell = cell;
	writeLog("mouse down, " + cell.x);
}

function generic_onMouseUp(){
	if(clickedCell != null){
		document.getElementById("dragCell").style.display = "NONE";
		clickedCell.style.backgroundColor = "#FFFFFF";
		clickedCell = null;
	}
	writeLog("mouse up");	
}

function generic_onMouseMove(){
	if(clickedCell != null){
		document.getElementById("dragCell").style.left = event.clientX - 100;
		document.getElementById("dragCell").style.top = event.clientY - 100;
		
		writeLog(document.getElementById("testTable").style.left);
		
		writeLog(event.clientX + ", " + event.clientY);
	}	
}

function generic_onMouseOver(cell){
	cell.style.borderStyle = "dotted";
}

function generic_onMouseOut(cell){
	cell.style.borderStyle = "groove";
}

function initTable(){
	var strHTML = "<TABLE BORDER=1 CELLPADDING=0 CELLSPACING=0>";
	
	for(var intRow = 0; intRow < 3; intRow++){
		strHTML += "<TR>";
		
		for(var intCol = 0; intCol < 3; intCol++){
//			strHTML += "<TD ID='cell_" + intRow + "_" + intCol + "' " +
//							"onMouseDown='generic_onMouseDown(this)' " +
//							"onMouseUp='generic_onMouseUp()' " + 
//							"onMouseMove='generic_onMouseMove()' >" + intRow + ", " + intCol + "</TD>";
							
			strHTML += "<TD ID='cell_" + intRow + "_" + intCol + "' " +
							"onMouseDown='generic_onMouseDown(this)' " +
							"onMouseOver='generic_onMouseOver(this)' " +
							"onMouseOut='generic_onMouseOut(this)' >" + intRow + ", " + intCol + "</TD>";
		}
		
		strHTML += "</TR>";
	}
	
	strHTML += "</TABLE>";
	
	document.getElementById("testTable").innerHTML = strHTML;
}

function writeLog(strText){
	document.getElementById("debugLog").innerHTML = Date() + " - " + strText + "<BR>" + document.getElementById("debugLog").innerHTML;
}

</SCRIPT>
</HEAD>
<BODY ONLOAD="page_onLoad()" ONMOUSEUP="generic_onMouseUp()" ONMOUSEMOVE="generic_onMouseMove()">

<DIV ID="testTable" CLASS="testTable"></DIV>
<BR>
<DIV ID="debugLog" CLASS="log"></DIV>

<DIV ID="dragCell" CLASS="dragCell"></DIV>

</BODY>
</HTML>
Ok. If you run this code (yes, I know it's IE only, but that's ok for now), you'll see that if you click on a cell, a new box apears that you can "drag" around the screen. I want to be able to drop that box somewhere else in the table and have the cell I "grabed" switch with the cell where I dropped it. Make sense? I just want to switch two cells via drag and drop.

My problem here is that because I've "grabing" a DIV, the mouse over events for the table behind the box do not fire. Thus, I have no way of knowing where they are droping the box. I do know what the coordinates of the mouse are, and I suppose I could calculate what cell they are hovering over based on those coordinates, but that would get pretty complicated if I have different sized cells - not to mention that the table might be at an unknown location on the screen and the cells might resize.

Is there a way to grab the coordinates of a specific cell? Or would it be best if instead of rendering a table, that I make a table out of DIVs? (I don't really want to do that, but it is an option).

I hope I've been clear enough. Do you guys have any spiffy ideas here?


---------------------
He who has knowledge spares his words, and a man of understanding is of a calm spirit. Even a fool is counted wise when he holds his peace; when he shuts his lips, he is considered perceptive. - King Solomon
 
Is it necessary for the mouse pointer to rest right in the middle of the dragged element? If not you could offset it by 1 pixel off the pointer and then you won't run into the mouseover problems.

Another problem that you'll run into when you offset the floating div is that you're going to be selecting the text on the page as you mouseover it. Use document.selection.clear() to take care of that.

I also threw in the bit to switch the contents of the cells. The response time isn't real great upon moving the mouse, but I'm sure that can be tweaked. Here's the code:
Code:
<HTML>
<HEAD>

<STYLE>
TR            {
            height: 200px;
            }
            
TD            {
            text-align: center;
            vertical-align: middle;
            width: 200px;
            }
            
.testTable    {
            left: 0px;
            position: absolute;
            top: 0px;
            }
            
.log        {
            border: 1px solid #000000;
            height: 100px;
            left: 0px;
            overflow: auto;
            position: absolute;
            top: 620px;
            width: 700px;
            }
            
.dragCell    {
            background-color: #555555;
            border: 1px solid #000000;
            height: 200px;
            filter: alpha(opacity=55);
            left: 0px;
            opacity: 0.05;
            position: absolute;
            top: 0px;
            width: 200px;
            Z-Index: 2;
            }
            
</STYLE>
<SCRIPT type=text/javascript>

var clickedCell = null;
[!]var clickedContent = "";
var targetCell = null;[/!]

function page_onLoad(){
    document.getElementById("dragCell").style.display = "NONE";
    initTable();
}

function generic_onMouseDown(cell){
    document.getElementById("dragCell").style.display = "BLOCK";
    document.getElementById("dragCell").style.left = event.clientX[!] + 1[/!];
    document.getElementById("dragCell").style.top = event.clientY[!] + 1[/!];
    
    cell.style.backgroundColor = "#999999";
    clickedCell = cell;
    [!]clickedContent = clickedCell.innerHTML;[/!]
    writeLog("mouse down, " + cell.x);
}

function generic_onMouseUp(){
    if(clickedCell != null){
        document.getElementById("dragCell").style.display = "NONE";
        clickedCell.style.backgroundColor = "#FFFFFF";
[!]        if (targetCell != null) {
           clickedCell.innerHTML = targetCell.innerHTML;
           targetCell.innerHTML = clickedContent;
        }[/!]
    }
 [!]   //clear state variables
    clickedContent = "";
    clickdCell = null;
    targetCell = null;[/!]
    writeLog("mouse up");    
}

function generic_onMouseMove(){
    if(clickedCell != null){
        document.getElementById("dragCell").style.left = event.clientX[!] + 1[/!];
        document.getElementById("dragCell").style.top = event.clientY[!] + 1[/!];
        
        writeLog(document.getElementById("testTable").style.left);
        
        writeLog(event.clientX + ", " + event.clientY);
    }
[!]    document.selection.clear();[/!]
}

function generic_onMouseOver(cell){
    cell.style.borderStyle = "dotted";
    [!]targetCell = cell;[/!]
}

function generic_onMouseOut(cell){
    cell.style.borderStyle = "groove";
    [!]targetCell = null;[/!]
}

function initTable(){
    var strHTML = "<TABLE BORDER=1 CELLPADDING=0 CELLSPACING=0>";
    
    for(var intRow = 0; intRow < 3; intRow++){
        strHTML += "<TR>";
        
        for(var intCol = 0; intCol < 3; intCol++){
//            strHTML += "<TD ID='cell_" + intRow + "_" + intCol + "' " +
//                            "onMouseDown='generic_onMouseDown(this)' " +
//                            "onMouseUp='generic_onMouseUp()' " +
//                            "onMouseMove='generic_onMouseMove()' >" + intRow + ", " + intCol + "</TD>";
                            
            strHTML += "<TD ID='cell_" + intRow + "_" + intCol + "' " +
                            "onMouseDown='generic_onMouseDown(this)' " +
                            "onMouseOver='generic_onMouseOver(this)' " +
                            "onMouseOut='generic_onMouseOut(this)' >" + intRow + ", " + intCol + "</TD>";
        }
        
        strHTML += "</TR>";
    }
    
    strHTML += "</TABLE>";
    
    document.getElementById("testTable").innerHTML = strHTML;
}

function writeLog(strText){
    document.getElementById("debugLog").innerHTML = Date() + " - " + strText + "<BR>" + document.getElementById("debugLog").innerHTML;
}

</SCRIPT>
</HEAD>
<BODY ONLOAD="page_onLoad()" ONMOUSEUP="generic_onMouseUp()" ONMOUSEMOVE="generic_onMouseMove()">

<DIV ID="testTable" CLASS="testTable"></DIV>
<BR>
<DIV ID="debugLog" CLASS="log"></DIV>

<DIV ID="dragCell" CLASS="dragCell"></DIV>

</BODY>
</HTML>

-kaht

[small]How spicy would you like your chang sauce? Oh man... I have no idea what's goin' on right now...[/small]
[banghead]
 
That's awsome kaht! Thanks a bunch man.

I did notice the draging over the text, which is why I initially wanted my cursor in the middle of the square. But with that selection.clear() method, I think it'll work nicely!

(testing code)...

Ok, I see where it's a bit laggy, and it looks like it's with the document.selection.clear() method. Is this the only way to clear the selection? I tried cell.selection.clear() in the mouseOver method, and that doesn't work.

Is it possible to cancel the mouseOver method for the browser (or whatever actually does the highlighting of the text on a drag)?


---------------------
He who has knowledge spares his words, and a man of understanding is of a calm spirit. Even a fool is counted wise when he holds his peace; when he shuts his lips, he is considered perceptive. - King Solomon
 
I have not looked over your code to determine how it works so this may or may not be a valid suggestion but...

What you could do rather than dragging the actual object is to use onclick events on some objects to start the drag effect and rather than dragging that object just display a ghosted box under the mouse and perhaps do something with the background color of the area you are dragging from.
Then you drag the ghosted box rather than the actual object and when you release it you test to see if the current coordinates are in range of a location to drop it into then behind the scenes you do a quick swap of the info displayed in those areas.
Using the ghosted version of the box you can set it absolutely positioned to the mouse pointers position and not worry about using the onmouseover event.

Just thoughts, I have not actually tried this method yet but it sounds like it might clear up performance issues, text highlighting issues (which have to be handled differently in different browsers) and the onmouse events.



It's hard to think outside the box when I'm trapped in a cubicle.
 
Hey, it's not the selection.clear, it's the writing to the log that's slowing things up! I commented out the log stuff and it works great!

Hey niteowl, thanks for the suggestion. I think I understand what your suggestion is, and it seems to work pretty well. I'd prefer that they click and drag as that's what most people are used to, but your idea was something that I hadn't thought of. Here is the code that I came up with for your suggestion if you wanted to verify it (don't have to read code, just test if you like).

Code:
<HTML>
<HEAD>

<STYLE>
TR            {
            height: 200px;
            }
            
TD            {
            text-align: center;
            vertical-align: middle;
            width: 200px;
            }
            
.testTable    {
            left: 0px;
            position: absolute;
            top: 0px;
            }
            
.log        {
            border: 1px solid #000000;
            height: 100px;
            left: 0px;
            overflow: auto;
            position: absolute;
            top: 620px;
            width: 700px;
            }
            
.dragCell    {
            background-color: #555555;
            border: 1px solid #000000;
            height: 200px;
            filter: alpha(opacity=55);
            left: 0px;
            opacity: 0.05;
            position: absolute;
            top: 0px;
            width: 200px;
            Z-Index: 2;
            }
            
</STYLE>
<SCRIPT type=text/javascript>

var clickedCell = null;
var clickedContent = "";
var targetCell = null;

function page_onLoad(){
    document.getElementById("dragCell").style.display = "NONE";
    initTable();
}

function generic_onMouseDown(cell){
	if(clickedCell == null){
	    document.getElementById("dragCell").style.display = "BLOCK";
	    document.getElementById("dragCell").style.left = event.clientX + 1;
	    document.getElementById("dragCell").style.top = event.clientY + 1;
	    
		cell.style.backgroundColor = "#999999";
		clickedCell = cell;
		clickedContent = clickedCell.innerHTML;
	}else{
        document.getElementById("dragCell").style.display = "NONE";
        clickedCell.style.backgroundColor = "#FFFFFF";
        if (targetCell != null) {
           clickedCell.innerHTML = targetCell.innerHTML;
           targetCell.innerHTML = clickedContent;
        }
        
            //clear state variables
	    clickedContent = "";
	    clickdCell = null;
	    targetCell = null;
	}
	
    writeLog("mouse down, " + cell.x);
}

function generic_onMouseUp(){
/*    if(clickedCell != null){
        document.getElementById("dragCell").style.display = "NONE";
        clickedCell.style.backgroundColor = "#FFFFFF";
        if (targetCell != null) {
           clickedCell.innerHTML = targetCell.innerHTML;
           targetCell.innerHTML = clickedContent;
        }
    }
    //clear state variables
    clickedContent = "";
    clickdCell = null;
    targetCell = null;
    writeLog("mouse up");*/
}

function generic_onMouseMove(){
    if(clickedCell != null){
        document.getElementById("dragCell").style.left = event.clientX + 1;
        document.getElementById("dragCell").style.top = event.clientY + 1;
        
//        writeLog(document.getElementById("testTable").style.left);
//        writeLog(event.clientX + ", " + event.clientY);
    }
    //document.selection.clear();
}

function generic_onMouseOver(cell){
    //cell.style.borderStyle = "dotted";
    targetCell = cell;
}

function generic_onMouseOut(cell){
    //cell.style.borderStyle = "groove";
    targetCell = null;
}

function initTable(){
    var strHTML = "<TABLE BORDER=1 CELLPADDING=0 CELLSPACING=0>";
    
    for(var intRow = 0; intRow < 3; intRow++){
        strHTML += "<TR>";
        
        for(var intCol = 0; intCol < 3; intCol++){
//            strHTML += "<TD ID='cell_" + intRow + "_" + intCol + "' " +
//                            "onMouseDown='generic_onMouseDown(this)' " +
//                            "onMouseUp='generic_onMouseUp()' " +
//                            "onMouseMove='generic_onMouseMove()' >" + intRow + ", " + intCol + "</TD>";
                            
            strHTML += "<TD ID='cell_" + intRow + "_" + intCol + "' " +
                            "onMouseDown='generic_onMouseDown(this)' " +
                            "onMouseOver='generic_onMouseOver(this)' " +
                            "onMouseOut='generic_onMouseOut(this)' >" + intRow + ", " + intCol + "</TD>";
        }
        
        strHTML += "</TR>";
    }
    
    strHTML += "</TABLE>";
    
    document.getElementById("testTable").innerHTML = strHTML;
}

function writeLog(strText){
    document.getElementById("debugLog").innerHTML = Date() + " - " + strText + "<BR>" + document.getElementById("debugLog").innerHTML;
}

</SCRIPT>
</HEAD>
<BODY ONLOAD="page_onLoad()" ONMOUSEUP="generic_onMouseUp()" ONMOUSEMOVE="generic_onMouseMove()">

<DIV ID="testTable" CLASS="testTable"></DIV>
<BR>
<DIV ID="debugLog" CLASS="log"></DIV>

<DIV ID="dragCell" CLASS="dragCell"></DIV>

</BODY>
</HTML>
Thanks for the feedback guys! This site never lets me down.


---------------------
He who has knowledge spares his words, and a man of understanding is of a calm spirit. Even a fool is counted wise when he holds his peace; when he shuts his lips, he is considered perceptive. - King Solomon
 
Oh I still expected you to do the click-drag, you just use an event handler to detect when the mouse button is released to execute the functions to drop the object. It would look to operate pretty much the same as the current method except for dragging a ghosted box instead of the actual object which stays where it sits and only gets altered when a successful drop has occured. A lot of programs do drag/drops this way rather than moving the actual object. When the drag is canceled or it is dropped someplace it should not be you just hide the div rather than having to move it back.


It's hard to think outside the box when I'm trapped in a cubicle.
 
When you say "ghosted box", are you refering to simply an outline of a box as opposed to a filled in box? Currently, when the user selects a table cell, a new DIV appears that is dragged, not the actual cell itself. Is that what you mean by "ghosted box"?

Sorry I'm thick headed. I just want to understand because this might be an idea worth exploring further!


---------------------
He who has knowledge spares his words, and a man of understanding is of a calm spirit. Even a fool is counted wise when he holds his peace; when he shuts his lips, he is considered perceptive. - King Solomon
 
Yes that is what I meant. By ghosted I just meant a subtle looking object to convey the idea that they are dragging the object and it will move to where they drop it when they stop dragging. It does not really have to be "ghosted", it could be an exact replica of the original if you wanted or whatever. A lot of programs when you drag an object just drag a wireframe of the object so you can see what/where your moving, others move the actual object, some show a subdued version like the original but maybe with the transparency modified to give it a subdued effect so you know visually which is the actual object and which one represents dragging.


It's hard to think outside the box when I'm trapped in a cubicle.
 
Ok, I think I'm with yah. That's actually what the code I have is doing right now. It displays a transparent box the same size as the original cell and it follows your curser as you drag while the original object stays there.

Thanks for the help!


---------------------
He who has knowledge spares his words, and a man of understanding is of a calm spirit. Even a fool is counted wise when he holds his peace; when he shuts his lips, he is considered perceptive. - King Solomon
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top