CSS
.selected
{
background-color: Red;
color: #ffffff;
}
jQuery
$(document).on('mousemove', '#mytable tr', function (e)
{
var currentColoumn = $(e.target).closest('td').index();
if ($(this).find("td").eq(currentColoumn).hasClass('selected') == true) {
e.preventDefault();
return false;
}
});
HTML
<table border="1" id="mytable ">
<tr>
<td>
9:30 AM
</td>
<td>
30
</td>
<td>
</td>
<td class="selected">
SELECTED
</td>
<td>
</td>
</tr>
</table>
我必须检查tr鼠标移动中的一个条件,如果td选择了类,则鼠标移动停止。如果鼠标快速移动时条件未执行
演示
此处使用on
的方式是将事件侦听器附加到文档,并将回调应用于#mytable
元素中行上的任何mousemove
事件。这意味着在内部,对on
的调用被转换为对delegate
的调用
通常情况下,我完全支持活动授权,原因很明显(表现是最明显的)。但是,当您委托鼠标事件时,每次触发事件时都会调用处理程序。鼠标什么时候不在document
中移动?当客户端正在阅读时,窗口最小化或不在焦点上。
尽管jQuery隐藏了这一点,但您的代码实际上是这样做的:
document.body.addEventListener('mousemove',function(e)
{
var parent, target = (e = e || window.event).target || e.srcElement;
if (target.tagName.toLowerCase() === 'tr')
{//this reverse DOM lookup is probably cached, but anyway
parent = target;
while(parent = parent.parentNode)
{//or something
if (parent.getAttribute('id') === 'myTable')
{
break;
}
if (parent === document.body)
{//not child of #myTable
return e;
}
}
if (yourCallbackFunction.apply(target, [e]) === false)
{//remember: return false in jQ == preventDefault + stopPropagation:
e.preventDefault();
e.stopPropagation();
}
}
}, false);
只有当元素是#myTable
的子元素时才会调用回调,但每次鼠标移动时,都会调用一个事件处理程序
当委派这样的事件时,更明智的做法是:
$('#myTable').on('mousemove', 'tr', function()
{
//your code here
});
这样,只有在#myTable
元素内部发生mousemove事件时,才会调用事件处理程序。既然这就是你感兴趣的全部,那就是你应该做的
您链接到的小提琴也包含live
调用。live
已经被弃用多年了,其中一个原因是:它太慢了
如果速度是一个问题,另一件需要研究的事情是:避免过多的DOM调用,例如使用闭包。DOM调用很慢!
如果速度仍然是一个问题,不要使用jQuery:如果做得好,请使用VanillaJs。。。它总是比lib快
我认为这种方法只需要一些调整。可以在单击单元格时计算约束条件;当鼠标移动时,你知道何时停止给细胞着色。
下面是一个细分:
jQuery(function($) {
document.onselectstart = function () { return false; }
var $table = $('#contentPlaceHolderMain_tableAppointment'),
columns = [],
dragInfo = null;
$table.find('td').each(function() {
var i = $(this).index();
(columns[i] = columns[i] || []).push(this);
});
这将创建一个数据透视数组,以便您可以更容易地引用每一列。这稍后会派上用场。
$table.on('mouseup', function() {
// reset selection
dragInfo = null;
});
这与您以前的代码相同,只是您可能注意到了两个差异:
我在
$table
而不是document
上设置点击事件处理程序;当接收到表外的点击时,这可以防止一些开销。由于
.live()
已弃用,因此应改用.on()
。
让我们转到mousedown
处理程序。
$table.on('mousedown', 'td', function() {
var $this = $(this),
columnIndex = $this.index();
if ($this.is('.selected') || columnIndex < 2) {
return;
}
var thisRow = $this.parent().index(),
selectionRowAbove = -Infinity,
selectionRowBelow = Infinity;
$.each(columns[columnIndex], function(rowIndex) {
if ($(this).is('.selected')) {
if (rowIndex < thisRow && rowIndex > selectionRowAbove) {
selectionRowAbove = rowIndex;
} else if (rowIndex > thisRow && rowIndex < selectionRowBelow) {
selectionRowBelow = rowIndex;
}
}
});
通过在td
而不是tr
上添加侦听器,您不必找到最近的单元格;它是为你计算的。函数的这一部分计算出当前单元格上方和下方的红色单元格。
// unmark cells
$table.find(".csstdhighlight").removeClass("csstdhighlight");
dragInfo = {
column: columnIndex,
enclosure: {
above: selectionRowAbove,
below: selectionRowBelow
},
min: thisRow,
max: thisRow
};
$this.addClass('csstdhighlight');
});
该功能的最后一部分保存了您稍后需要的所有拖动信息
$table.on('mousemove', 'td', function() {
if (!dragInfo) {
return;
}
var $this = $(this),
columnIndex = $this.index(),
rowIndex = $this.parent().index();
if (columnIndex != dragInfo.column || rowIndex == 0 || rowIndex <= dragInfo.enclosure.above || rowIndex >= dragInfo.enclosure.below) {
dragInfo = null;
return;
}
这些条件确保选择保持在我们之前确定的范围内。
// mark cells
var cells = [columns[columnIndex][rowIndex]];
while (dragInfo.min > rowIndex) {
cells.push(columns[columnIndex][dragInfo.min--]);
}
while (dragInfo.max < rowIndex) {
cells.push(columns[columnIndex][dragInfo.max++]);
}
$(cells).addClass('csstdhighlight');
});
});
功能的最后一部分标记选择;它通过计算上次调用之间的差异来实现这一点,这样您就不必再次标记单元格。
Demo
然而,有一个半解决方案是可以做到的。
在鼠标体上使用CSS隐藏实际的鼠标光标,然后在鼠标位置显示一个假鼠标指针(只是一个常规图形)。
如果鼠标指针超出边界,请将假光标停止在边界框处。
我不知道你的用例是什么,但如果你不隐藏真正的鼠标指针,而是在边界框中显示某种超大的鼠标指针会更好。大多数情况下效果相同,而且更加用户友好。
考虑一种不同的方法来决定鼠标移动是否应该停止突出显示单元格。不是"在经过选定类的单元格时停止"
如果在初始单元格和当前单元格之间存在选定类的单元格,请尝试停止。这样可以确保,如果快速移动阻止mousemove事件被委派到"选定"的单元格,它仍然能够检测到您已经越过了一个单元格。
我认为这里的主要问题是鼠标没有在每个单元格上停止。鼠标通常看起来是连续移动的,但当我快速移动时,它会跳来跳去,跳过中间的大量空间。
我想你已经看到了这种影响。因此,高亮显示的是一系列单元格,而不仅仅是指向的单元格(当前单元格)。但是,您只检查当前单元格是否被选中,而不是是否选择了任何中间单元格。
解决方案是检查所有中间单元格,以确保它们未被选中。jQuery有一个filter
函数可以很好地做到这一点。我已经重构了你的代码以使用过滤器:
$("#contentPlaceHolderMain_tableAppointment tr").live('mousemove', function (e) {
// Compares with the last and next row index.
var currentRow = $(this).closest("tr")[0].rowIndex;
var currentColoumn = $(e.target).closest('td').index();
var allRows = $('#contentPlaceHolderMain_tableAppointment tr');
var previousRowIndex = parseInt(lastRow, 10);
var currentRowIndex = parseInt(currentRow, 10);
var traversedRows;
if (previousRowIndex < currentRowIndex)
traversedRows = allRows.slice(previousRowIndex, parseInt(currentRowIndex + 1));
else {
traversedRows = allRows.slice(currentRowIndex, previousRowIndex)
}
var affectedCells = traversedRows.children(':nth-child(' + parseInt(currentColoumn + 1) + ')');
if ($(this).find("td").eq(currentColoumn).hasClass('selected') == true ||
affectedCells.filter('.selected').length > 0) {
if (flag != false) {
flag = false;
e.preventDefault();
return false;
}
}
if (currentRow == 0) {
flag = false;
return false;
}
//cross cell selection.
if (colIndex != currentColoumn) {
flag = false;
return;
}
if (flag) {
affectedCells.addClass('csstdhighlight');
e.preventDefault();
return false;
}
});
小提琴在:http://jsfiddle.net/Jg58G/7/
仍然存在这样一个问题,即一些单元格本应被突出显示,但事实并非如此。既然已经有了所选单元格的行,就可以重做切片逻辑,以便在正确的位置进行切片。我把那部分留给你。
请注意,如果这正是您的目标,但应该很容易适应。使用mouseover可以减少调用,应该会有更好的性能。
在:http://jsfiddle.net/VJG4F/
$(document).ready(function () {
// setup
var doc = this;
doc.checkOver = false;
doc.activeCell = {
x: -1,
y: -1
};
doc.lastCell = {
x: -1,
y: -1
};
doc.highlightClass = 'csstdhighlight';
doc.selectionClass = 'selected';
doc.selectionText = 'SELECTED';
// start checking on mousedown
$('#contentPlaceHolderMain_tableAppointment td').on('mousedown', function (e) {
doc.checkOver = true;
// set active and last cell for reference
doc.lastCell.x = doc.activeCell.x = $(this).closest('td').index();
doc.lastCell.y = doc.activeCell.y = $(this).closest("tr").index();
// highlight selected cells
var cellSelector = '#contentPlaceHolderMain_tableAppointment tr:eq(' + doc.activeCell.y + ') td:eq(' + doc.activeCell.x + ')';
$(cellSelector).addClass(doc.highlightClass);
// check for movement
$('#contentPlaceHolderMain_tableAppointment td').on('mouseover', function (e) {
if (!(doc.checkOver)) return;
// get current cell for reference
var currCell = {
x: $(e.target).closest('td').index(),
y: $(e.target).closest('tr').index()
};
// verify mouse is over a different cell
if ( !((currCell.y != doc.lastCell.y && currCell.y != -1) || (currCell.x != doc.lastCell.x && currCell.x != -1)) ) return false;
// make sure other cells are not highlighted
$('#contentPlaceHolderMain_tableAppointment td').removeClass(doc.highlightClass);
// highlight selected cells
var topLeft = {
x: Math.min(currCell.x, doc.activeCell.x),
y: Math.min(currCell.y, doc.activeCell.y)
};
var botRight = {
x: Math.max(currCell.x, doc.activeCell.x),
y: Math.max(currCell.y, doc.activeCell.y)
};
for (var x=topLeft.x;x<=botRight.x;x++) {
for (var y=topLeft.y;y<=botRight.y;y++) {
var cellSelector = '#contentPlaceHolderMain_tableAppointment tr:eq(' + y + ') td:eq(' + x + ')';
$(cellSelector).addClass(doc.highlightClass);
}
}
// update last cell
doc.lastCell.y = currCell.y;
doc.lastCell.x = currCell.x;
return false;
});
// check for mouseup
$('#contentPlaceHolderMain_tableAppointment td').on('mouseup', function (e) {
// stop checking for movement
$('#contentPlaceHolderMain_tableAppointment td').off('mouseup');
$('#contentPlaceHolderMain_tableAppointment td').off('mouseout');
// get current cell for reference
var currCell = {
x: $(this).closest("td").index(),
y: $(this).closest('tr').index()
};
// make sure cells are not highlighted
$('#contentPlaceHolderMain_tableAppointment td').removeClass(doc.highlightClass);
// select cells
var topLeft = {
x: Math.min(currCell.x, doc.activeCell.x),
y: Math.min(currCell.y, doc.activeCell.y)
};
var botRight = {
x: Math.max(currCell.x, doc.activeCell.x),
y: Math.max(currCell.y, doc.activeCell.y)
};
// single cell - toggle
if( topLeft.x == botRight.x && topLeft.y == botRight.y ) {
var cellSelector = '#contentPlaceHolderMain_tableAppointment tr:eq(' + topLeft.y + ') td:eq(' + topLeft.x + ')';
$(cellSelector).toggleClass(doc.selectionClass);
if( $(cellSelector).text() == doc.selectionText ) $(cellSelector).text('');
else $(cellSelector).text(doc.selectionText);
// multi-cell, select all
} else {
for (var x=topLeft.x;x<=botRight.x;x++) {
for (var y=topLeft.y;y<=botRight.y;y++) {
var cellSelector = '#contentPlaceHolderMain_tableAppointment tr:eq(' + y + ') td:eq(' + x + ')';
$(cellSelector).addClass(doc.selectionClass);
$(cellSelector).text(doc.selectionText);
}
}
}
// reset
doc.checkOver = false;
doc.activeCell.y = -1;
doc.activeCell.x = -1;
doc.lastCell.y = -1;
doc.lastCell.x = -1;
return false;
});
return false; // prevent default
});
});