4

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 选择了类,则鼠标移动停止。如果鼠标快速移动时条件不执行

演示

4

6 回答 6

3

您在on此处使用的方式是将事件侦听器附加到文档,并将回调应用于元素mousemove中行上的任何事件。#mytable这意味着在内部,对的调用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);

只有当元素是 a 的子元素时才会调用您的回调#myTable,但每次鼠标移动时,都会调用事件处理程序
在委派这样的事件时,这样做会更明智:

$('#myTable').on('mousemove', 'tr', function()
{
    //your code here
});

这样,只有当元素内部发生 mousemove 事件时才会调用事件处理程序。#myTable既然这就是你所感兴趣的,那就是你应该做的。

您链接到的小提琴也充满live电话。多年来live已被弃用,原因之一是:它太慢了。 如果速度是一个问题,另一件需要考虑的事情是:避免过多的 DOM 调用,例如使用闭包。DOM 调用很慢!

如果速度仍然是个问题,请不要使用 jQuery:使用 VanillaJs,如果做得对……它总是比 lib 快

于 2013-08-30T14:41:19.233 回答
1

我认为这种方法只需要一些调整。可以在单击单元格时计算约束;随着鼠标的移动,您知道何时停止对单元格着色。

这是一个细分:

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. 我正在设置 click 事件处理程序$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');
    });
});

函数的最后一部分标记了选择;它通过计算上次调用之间的差异来做到这一点,这样您就不必再次标记单元格。

演示

于 2013-09-04T07:11:01.577 回答
0

请注意确定这是否正是您的目标,但它应该很容易适应。使用鼠标悬停减少调用,应该有更好的性能。

小提琴: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
    });

});
于 2013-09-04T03:29:16.163 回答
0

我认为这里的主要问题是鼠标不会停在每个单元格上。鼠标通常看起来像是在连续移动,但是当我快速移动它时,它会四处跳动,在两者之间跳过大量空间。

我想你已经看到了这种影响。因此,您正在突出显示一系列单元格,而不仅仅是您指向的单元格(当前单元格)。但是,您只检查是否选择了当前单元格,而不检查是否选择了任何中间单元格。

解决方案是检查单元格之间的所有内容以确保它们未被选中。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/

仍然存在应该突出显示某些单元格的问题,但事实并非如此。现在您有了所选单元格的行,您可以重做切片逻辑以在正确的位置切片。我会把那部分留给你。

于 2013-09-02T23:24:04.847 回答
0

考虑一种不同的方法来决定鼠标移动是否应该停止突出显示单元格。而不是“当它越过选定类的单元格时停止”,

如果在初始单元格和当前单元格之间存在选择了类的单元格,请尝试停止。这可以确保如果快速移动阻止 mousemove 事件被委托给您的“选定”单元格,它仍然能够检测到您已经通过了一个单元格。

于 2013-08-30T15:08:02.303 回答
0

然而,有一种半解决方案可以做到。

在 body 上使用 CSS 隐藏实际的鼠标光标,然后在鼠标位置显示一个假鼠标指针(只是一个常规图形)。

如果鼠标指针超出范围,则将假光标停止在边界框处。

我不知道您的用例是什么,但是如果您不隐藏真正的鼠标指针,而是在边界框中显示某种超大鼠标指针,那会更好大部分效果相同,并且更加用户友好。

于 2013-08-14T09:07:55.187 回答