Why do mouseup events sometimes fire if the mouse is not released?

M

markszlazak

In the following script, a control displays (black box) in each table
cell once you mouse over the cell. Mouse down on the control to change
the mode of the table. Drag the mouse over cells in the same column
then mouseup anywhere in a cell. The mouseup event sometimres fires
before the selection of table cells by dragging is complete. It's
important that I stop these "false" mouseup's from firing or
distinguish them from when I let go of the mouse button.

<html>
<head>
<title></title>
<style>
#calendar {
position:relative;
top:10;
left:10;
background-color:white;
width:700;
}
.calendarHeader {
background-color:lightgreen;
text-align:center;
font-family:sans-serif;
width:347;
}
.calendarRow {
height:30;
}
#book {
position:absolute;
width:400;
height:200;
color:grey;
background-color:lightyellow;
border:3px grey solid;
font-weight:bold;
font-family:sans-serif;
visibility:hidden;
}
#output {
position:relative;
top:30;
left:10
}
#clear {
position:relative;
top:40;
left:10;
width:80;
background-color:lightgrey;
border:2px black solid;
color:black;
text-align:center;
font-weight:bold;
font-family:sans-serif;
}
</style>
</head>
<body>
<table id='calendar' border='1' cellpadding='0' cellspacing='0'>
<tr>
<td class="calendarHeader">Mark</td>
<td class="calendarHeader">Jennie</td>
</tr>
<tr class='calendarRow'>
<td id='1|Mark'></td>
<td id='1|Jennie'></td>
</tr>
<tr class='calendarRow'>
<td id='2|Mark'></td>
<td id='2|Jennie'></td>
</tr>
<tr class='calendarRow'>
<td id='3|Mark'></td>
<td id='3|Jennie'></td>
</tr>
<tr class='calendarRow'>
<td id='4|Mark'></td>
<td id='4|Jennie'></td>
</tr>
<tr class='calendarRow'>
<td id='5|Mark'></td>
<td id='5|Jennie'></td>
</tr>
<tr class='calendarRow'>
<td id='6|Mark'></td>
<td id='6|Jennie'></td>
</tr>
<tr class='calendarRow'>
<td id='7|Mark'></td>
<td id='7|Jennie'></td>
</tr>
</table>

<div id="output"><textarea cols=50 rows=10></textarea></div>
<div id="clear"
onclick="document.getElementById('output').firstChild.value =
'';">CLEAR</div>
<div id="book" onclick="this.style.visibility = 'hidden';">&nbsp;</
div>

<script>
var calendar = document.getElementById('calendar'),
slots = calendar.getElementsByTagName('td'),
rows = calendar.getElementsByTagName('tr'),
/*debug*/ output = document.getElementById('output').firstChild;
/*debug*/ output.value = '';

var apt = document.getElementById('book');
apt.appendChild(document.createTextNode(''));

function addWebControl(el) {
var wc = document.createElement('div');
wc.setAttribute('style',
'position:relative; ' +
'background-color:lightgrey;' +
'border-width:1px;' +
'border-color:black; ' +
'border-style:solid; ' +
'margin:0px; ' +
'padding:0px; ' +
'visibility:hidden; ' +
'top:0; ' +
'left:312; ' +
'height:10; ' +
'width:30;');
el.appendChild(wc);
}

var firstSlotSelected = null,
firstInitialState,
drag = false,
columnSelected,
firstRowSelected,
lastRowSelected;

function mousedownHandler(e) { // Handler only on web access control
button inside slot.
var target = getEventTarget(e);

drag = true;
firstInitialState = target.style.backgroundColor;
firstSlotSelected = target.parentNode;
columnSelected = firstSlotSelected.cellIndex;
firstRowSelected = firstSlotSelected.parentNode.rowIndex;

var wcs, bgC = (firstInitialState == 'lightgrey')?
'lightgrey':'white';
target.parentNode.style.backgroundColor = bgC;
(wcs = target.parentNode.firstChild.style).backgroundColor =
(firstInitialState == 'lightgrey')? 'white':'lightgrey';
wcs.visibility = 'hidden';

return false;
}

var highlightedSlot = null;

function mouseoverHandler(e) {
var target = getEventTarget(e);
if (target.nodeName != 'TD') return; // Mouse did not move into
table cell.

/** Find the element the mouse moved from **/

var relTarget = (!e)? null:(e.relatedTarget)?
e.relatedTarget:e.fromElement; // Get the element the mouse comes
from.
if (!relTarget) return;

while (relTarget != target && relTarget.nodeName != 'BODY') { // Move
through ancestors until target or BODY found.
relTarget = relTarget.parentNode;
}

if (relTarget == target) return; // Mouse moves from a child
(related target) over the target element.

/** Mouse actually entered TD. Handle event. **/

if (!drag) {
if (highlightedSlot) { // Corrects for mouseout failing to
register with rapid cursor movement.
highlightedSlot.style.backgroundColor =
(highlightedSlot.firstChild.style.backgroundColor == 'lightgrey')?
'white':'lightgrey';
highlightedSlot.firstChild.style.visibility = "hidden";
}
highlightedSlot = target;
target.style.backgroundColor =
(target.firstChild.style.backgroundColor == 'lightgrey')?
'lightyellow':'lightgrey';
target.firstChild.style.visibility = "visible";
}
else {
if (target.cellIndex == columnSelected) {
lastRowSelected = target.parentNode.rowIndex;
var wcs, bgC = (firstInitialState == 'lightgrey')?
'lightgrey':'white';
target.style.backgroundColor = bgC;
(wcs = target.firstChild.style).backgroundColor =
(firstInitialState == 'lightgrey')? 'white':'lightgrey';
wcs.visibility = 'hidden';
}
else {
firstSlotSelected = null;
firstInitialState = null;
firstRowSelected = null;
lastRowSelected = null;
columnSelected = null;
}
}
}

function mouseoutHandler(e) {
var target = getEventTarget(e);
if (target.nodeName != 'TD') return; // Mouse did not move out of
table cell.

/** Find the element the mouse moved to. **/

var relTarget = (!e)? null:(e.relatedTarget)?
e.relatedTarget:e.toElement; // Get the element the mouse goes to.
if (!relTarget) return;

while (relTarget != target && relTarget.nodeName != 'BODY') { // Move
through ancestors until target or BODY found.
relTarget = relTarget.parentNode;
}

if (relTarget == target) return; // Mouse moves to a child (related
target) out of the target element.

/** Mouse actually left TD. Handle event. **/

if (drag) {
target.style.backgroundColor =
(target.firstChild.style.backgroundColor == 'lightgrey')?
'white':'lightgrey';
}
else {
highlightedSlot = null;
target.style.backgroundColor =
(target.firstChild.style.backgroundColor == 'lightgrey')?
'white':'lightgrey';
target.firstChild.style.visibility = "hidden";
}
}

function mouseupHandler(e) {
var target = getEventTarget(e);
if (target.nodeName == 'DIV') target = target.parentNode;

/*debug*/ output.value += 'mouseUp\t';

if (drag) {
firstSlotSelected = null;
firstInitialState = null;
firstRowSelected = null;
lastRowSelected = null;
columnSelected = null;
drag = false;
}
else if (target.style.backgroundColor != 'lightgrey' &&
getEventCoordinateX(e) - getElementCoordinateX(target) < 310) {
apt.firstChild.replaceData(1,10,target.id);
apt.style.left = e.clientX + window.pageXOffset;
apt.style.top = e.clientY + window.pageYOffset;
apt.style.visibility = 'visible';
}
}

function getElementCoordinateX(el) {
var x = 0;
while (el) {
x += el.offsetLeft;
el = el.offsetParent;
}
return x;
}

function getEventCoordinateX(e) {
return (typeof e.offsetX != 'undefined')? e.offsetX:e.clientX +
window.pageXOffset;
}

function addEvent(el, evtType, listener, captures) {
if (el.addEventListener) {
el.addEventListener(evtType, listener, captures);
return true;
}
else if (el.attachEvent) {
return el.attachEvent('on' + evtType, listener);
}
else {
el['on' + evtType] = listener;
}
}

function getEventTarget(e) {
return (window.event)? window.event.srcElement:(e)? e.target:null;
}

for (var i=0; i < slots.length; i++) {
if (slots.id.length > 0) {
addWebControl(slots); // Add web control DIV element as second
child;
addEvent(slots, 'mouseout', mouseoutHandler, false);
addEvent(slots, 'mouseover', mouseoverHandler, false);
addEvent(slots, 'mouseup', mouseupHandler, false);
addEvent(slots.firstChild, 'mousedown', mousedownHandler, false);
}
}
</script>
</body>
</html>
 
S

Stevo

In the following script, a control displays (black box) in each table
cell once you mouse over the cell. Mouse down on the control to change
the mode of the table. Drag the mouse over cells in the same column
then mouseup anywhere in a cell. The mouseup event sometimres fires
before the selection of table cells by dragging is complete. It's
important that I stop these "false" mouseup's from firing or
distinguish them from when I let go of the mouse button.

You might be suffering from key bounce. It happens with switches all the
time. You push a button and think you're making contact once and keeping
the button down you'd expect no mouse up. But, the nature of switches is
that you often get a bounce like a dropped ball doesn't just hit the
ground and stay there, it bounces a few times before settling. Same deal
with switches. I had to build debounce logic into a keyboard driver I
wrote once, although that was in assembler and had a lot more control
over timing than JavaScript would have. The basic principle is that you
ignore up events for a certain amount of time because you know they're
just the bounces. Then after that "blocking" time (we're talking a very
short time here), you look at the state, wait a bit more, check it
again, and then you can be sure of the final state and re-enable
(unblock) the up events. If you see the state is up, then you can
generate an up event (or call your up event handler) because you've
detected a genuine up event.

Now whether all this is coming into play in your situation, and whether
even if it is, you have the level of control that would be needed is a
different story. It's possible of course that Windows already takes care
of such issues. I'm throwing it out there though, and it was fun to
reminisce about the old days of Assembler to myself :)
 
M

markszlazak

You might be suffering from key bounce. It happens with switches all the
time. You push a button and think you're making contact once and keeping
the button down you'd expect no mouse up. But, the nature of switches is
that you often get a bounce like a dropped ball doesn't just hit the
ground and stay there, it bounces a few times before settling. Same deal
with switches. I had to build debounce logic into a keyboard driver I
wrote once, although that was in assembler and had a lot more control
over timing than JavaScript would have. The basic principle is that you
ignore up events for a certain amount of time because you know they're
just the bounces. Then after that "blocking" time (we're talking a very
short time here), you look at the state, wait a bit more, check it
again, and then you can be sure of the final state and re-enable
(unblock) the up events. If you see the state is up, then you can
generate an up event (or call your up event handler) because you've
detected a genuine up event.

Now whether all this is coming into play in your situation, and whether
even if it is, you have the level of control that would be needed is a
different story. It's possible of course that Windows already takes care
of such issues. I'm throwing it out there though, and it was fun to
reminisce about the old days of Assembler to myself :)

OK thanks, that is something to look at. So I take it that you don't
see an obvious problem with the code.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,968
Messages
2,570,150
Members
46,697
Latest member
AugustNabo

Latest Threads

Top