如果鼠标快速移动时条件未执行



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;
    });

这与您以前的代码相同,只是您可能注意到了两个差异:

  1. 我在$table而不是document上设置点击事件处理程序;当接收到表外的点击时,这可以防止一些开销。

  2. 由于.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
    });
});

最新更新