gpt4 book ai didi

javascript - 拖放 API 和表格行(仅限 vanilla JS)

转载 作者:行者123 更新时间:2023-12-04 12:35:38 24 4
gpt4 key购买 nike

在我的一列 n 行的 HTML 表中,例如:

<table id="my-table">
<tr>
<td>How</td>
</tr>
<tr>
<td>Are</td>
</tr>
<tr>
<td>You</td>
</tr>
</table>

我想让每一行都可以拖放到另一行中。如果将 A 行放入另一行 B,则应交换这些相应行的 HTML 内容。

为此,我阅读了相当多的文档和教程,但似乎缺少一些我无法弄清楚的东西。到目前为止,我所做的是将以下代码附加到每个 <td>标签:
draggable="true" ondragstart="dragCell(event)" ondragover="allowDrop(event)" ondrop="dropCell(event)" ondragenter="handleDragEnter(event)" ondragleave="handleDragLeave(event)" ondragend="handleDragEnd(event)"
我的 JavaScript 是:
/*##############################################################################
## 1. Dragstart Handler ##
##############################################################################*/

function dragCell(ev) {
let draggedRow = ev.target.closest("tr");
let rowNumber = draggedRow.rowIndex;
let tableId = draggedRow.parentNode.parentNode.getAttribute("id");
let data = {"id":tableId,"rowNumber":rowNumber};
let cellsdragged = draggedRow.children;
let amountOfCells = cellsdragged.length;
let dataToTransfer = [];
for (let i = 0; i < amountOfCells; i++) {
let currentCell = cellsdragged[i];
dataToTransfer.push(currentCell.outerHTML);
}
data["cellContents"] = dataToTransfer;
data = JSON.stringify(data);
ev.dataTransfer.setData("text/plain",data);
document.getElementsByTagName("body")[0].style.cursor = "grabbing";
}

/*##############################################################################
## 2. Dragover Handler ##
##############################################################################*/

function allowDrop(ev) {
ev.preventDefault();
}

/*##############################################################################
## 3. Drop Handler ##
##############################################################################*/

function dropCell(ev) {
ev.preventDefault();
if (ev.stopPropagation) {
ev.stopPropagation();
}
document.getElementsByTagName("body")[0].style.cursor = "default";
let data = ev.dataTransfer.getData("text/plain");
data = JSON.parse(data);
let rowToDrop = ev.target.closest('tr');
let targetIndex = rowToDrop.rowIndex;
let targetId = rowToDrop.parentNode.parentNode.getAttribute("id");
if (data.id == targetId && data.rowNumber != targetIndex) {
let targetContents = [];
let cellsForDrop = rowToDrop.children;
let amountOfCells = cellsForDrop.length;
for (let i = 0; i < amountOfCells; i++) {
targetContents
.push(cellsForDrop[i].outerHTML);
}
let draggedRow = document.getElementById(data.id).rows[data.rowNumber];
let cellsOfDrag = draggedRow.children;
for (let i = 0; i < amountOfCells; i++) {
cellsForDrop[i].outerHTML = data.cellContents[i][0];
cellsOfDrag[i].outerHTML = targetContents[i][0];
}
}
}

/*##############################################################################
## 4. Dragenter Handler ##
##############################################################################*/

function handleDragEnter(ev) {
ev.target.closest('tr').classList.add('ready-for-drop');
}

/*##############################################################################
## 5. Dragleave Handler ##
##############################################################################*/

function handleDragLeave(ev) {
ev.target.closest('tr').classList.remove('ready-for-drop');
}

/*##############################################################################
## 6. Dragend Handler ##
##############################################################################*/

function handleDragEnd(event) {
let rows = ev.target.closest("table").rows;
let amount = rows.length;
for (let i = 0; i < amount; i++) {
if (rows[i].classList.contains('ready-for-drop')) {
rows[i].classList.remove('ready-for-drop')
}
}
}

当我运行它并将一行拖到另一行时,控制台向我提供了一个关于 ev.target.closest 的错误。 ,对于 Dragenter、Dragleave 和 Dragend Handler 函数,总是说 ev.target.closest is not a function .尽管我在 dragstart 处理程序函数中使用完全相同的语法,但在该行的控制台中没有报告错误。拖完后,拖拽行的内容都变成 < .

我在这里想念什么?请考虑我不想使用任何 jQuery 解决方案,我想坚持使用 Vanilla JavaScript。还请考虑我确实需要整个 outerHTML要交换,因为我的 <td>标签可能包含特定于单元格的属性值。

最佳答案

好的想出了一个解决方案,最重要的事实:

  • 我用了addEventListener而不是 HTML 标记内的 HTML 内联事件监听器,如上所述,有几个原因(我不会在这里进一步说明)
  • 我遇到的最困难的问题是,当我将拖动的元素拖动到绑定(bind)了 dragenter 和 dragleave 监听器的元素的子元素上时,我的 dragleave 事件函数被触发。我在这个论坛上阅读了无数关于这个问题的文章和帖子,并且提出的解决方案似乎都不能轻松或简单地使用我非常基本的 HTML 表格行和单元格,并且很快就使用函数来停止事件传播等,实际上并非如此甚至是必要的,正如您将在我的代码中看到的那样。此外,我完全省略了 dragleave我的解决方案中的事件监听器,所以这可能是实现我想要的更简单的方法之一,这就是为什么我想在这里分享它给其他可能想要做我想做的类似事情的人。

  • 一切正常工作的不同之处在于:

    HTML:我只包含了 draggable="true" class="table-cell"td标签

    JavaScript:现在完全不同,但运行良好:
    /*##############################################################################
    ## 1. Define Function which adds all the required event listeners ##
    ##############################################################################*/

    function addDragEvents(element) {
    element.addEventListener("dragstart",dragCell);
    element.addEventListener("dragover",allowDrop);
    element.addEventListener("drop",dropCell);
    element.addEventListener("dragenter",handleDragEnter);
    element.addEventListener("dragend",handleDragEnd);
    }

    /*##############################################################################
    ## 2. Define Function which resets the drag state for the concerned table ##
    ##############################################################################*/

    function resetDragState(e) {
    let data = e.dataTransfer.getData("text/plain");
    data = JSON.parse(data);
    let rows = document.getElementById(data.id).rows;
    for (let row of rows) {
    if (row.classList.contains('ready-for-drop')) {
    row.classList.remove('ready-for-drop');
    }
    let cells = row.cells;
    for (let cell of cells) {
    cell.style.cursor="pointer";
    }
    }
    }

    /*##############################################################################
    ## 3. Add Required Event listeners to all table cells ##
    ##############################################################################*/

    let cells = document.getElementsByClassName('table-cell');
    for (let cell of cells) {
    addDragEvents(cell);
    }

    /*##############################################################################
    ## 4. Dragstart Handler ##
    ##############################################################################*/

    // This function is created to define the action of a drag (which data will be
    // taken for transfer to the HTML element into which the dragged element's
    // content will be dropped)

    function dragCell(e) {
    // Change the cursor to a grabbing hand on dragstart
    let cells = document.getElementsByClassName('table-cell');
    for (let cell of cells) {
    cell.style.cursor="grabbing";
    }
    // Three pieces of information must be transferred for the drag & drop to work
    // properly: the table id of the table having the row being dragged (to assure
    // that D&D only works among rows of the same table), the row index of the row
    // being dragged (to know which row needs to be replaced via the drop
    // function), and finally the content of the row being dragged
    let draggedRow = e.target.closest("tr");
    // Get the row index of that row
    let rowNumber = draggedRow.rowIndex;
    // Get the id name of the table having that row
    let tableId = draggedRow.parentNode.parentNode.getAttribute("id");
    // Initiate JSON object which will be transferred to the drop row
    let data = {"id":tableId,"rowNumber":rowNumber};
    // Append all the cells as second element onto this same object
    let cellsdragged = draggedRow.children;
    let amountOfCells = cellsdragged.length;
    let dataToTransfer = [];
    for (let i = 0; i < amountOfCells; i++) {
    let currentCell = cellsdragged[i];
    dataToTransfer.push(currentCell.outerHTML);
    }
    data["cellContents"] = dataToTransfer;
    data = JSON.stringify(data);
    e.dataTransfer.setData("text/plain",data);
    }

    /*##############################################################################
    ## 5. Dragover Handler ##
    ##############################################################################*/

    // This function is used to allow for drops into the corresponding HTML elements
    // (the default behavior doesn't allow this)
    function allowDrop(e) {
    e.preventDefault();
    }

    /*##############################################################################
    ## 6. Drop Handler ##
    ##############################################################################*/

    function dropCell(e) {
    // First, prevent default behavior once again
    e.preventDefault();
    // Second, access data coming from dragged element (which is the index of the
    // row from which data is being dragged)
    let data = e.dataTransfer.getData("text/plain");
    data = JSON.parse(data);
    // Next, get the index of the row into which content shall be dropped
    let rowToDrop = e.target.closest('tr');
    let targetIndex = rowToDrop.rowIndex;
    // Next, get the id of the table of that retrieved row
    let targetId = rowToDrop.parentNode.parentNode.getAttribute("id");
    // Next, only proceed if the dragged row comes from the same table as the
    // target row, and if the dragged and the target rows are two different rows
    if (data.id == targetId && data.rowNumber != targetIndex) {
    // Store the contents of the target row in the same array structure as the
    // one coming from the dragged row
    let targetContents = [];
    // Exchange the contents of the two rows
    let cellsForDrop = rowToDrop.children;
    let amountOfCells = cellsForDrop.length;
    for (let i = 0; i < amountOfCells; i++) {
    targetContents
    .push(cellsForDrop[i].outerHTML);
    }
    // Exchange the contents of the two rows
    let draggedRow = document.getElementById(data.id).rows[data.rowNumber];
    let cellsOfDrag = draggedRow.children;
    for (let i = 0; i < amountOfCells; i++) {
    // Replace the content of the row into which the drag is being dropped
    // with the content of the dragged row
    cellsForDrop[i].outerHTML = data.cellContents[i];
    // Replacement of the outerHTML deletes all bound event listeners, so:
    addDragEvents(cellsForDrop[i]);
    // And now, replace the content of the dragged row with the content of the
    // target row. Then, do the same for the value.
    cellsOfDrag[i].outerHTML = targetContents[i];
    addDragEvents(cellsOfDrag[i]);
    }
    resetDragState(e);
    }
    }

    /*##############################################################################
    ## 7. Dragenter Handler ##
    ##############################################################################*/

    // When dragging over the text node of a table cell (the text in a table cell),
    // while previously being over the table cell element, the dragleave event gets
    // fired, which stops the highlighting of the currently dragged cell. To avoid
    // this problem and any coding around to fight it, everything has been
    // programmed with the dragenter event handler only; no more dragleave needed

    // For the dragenter event, e.target corresponds to the element into which the
    // drag enters. This fact has been used to program the code as follows:

    var previousRow = null;

    function handleDragEnter(e) {
    // Assure that dragenter code is only executed when entering an element (and
    // for example not when entering a text node)
    if (e.target.nodeType === 1) {
    // Get the currently entered row
    let currentRow = this.closest('tr');
    // Check if the currently entered row is different from the row entered via
    // the last drag
    if (previousRow !== null) {
    if (currentRow !== previousRow) {
    // If so, remove the class responsible for highlighting it via CSS from
    // it
    previousRow.className = "";
    }
    }
    // Each time an HTML element is entered, add the class responsible for
    // highlighting it via CSS onto its containing row (or onto itself, if row)
    currentRow.className = "ready-for-drop";
    // To know which row has been the last one entered when this function will
    // be called again, assign the previousRow variable of the global scope onto
    // the currentRow from this function run
    previousRow = currentRow;
    }
    }

    /*##############################################################################
    ## 8. Dragend Handler ##
    ##############################################################################*/

    // This function is required for cases where the dragged has been dropped on a
    // non-valid drop target.

    function handleDragEnd(e) {
    resetDragState(e);
    }

    我将评论留在里面,因为我在网上进行了很多搜索(但没有成功)如何尽可能简单地使用拖放 API 使 HTML 表格的行/单元格可拖放,所以我希望这个答案会有所帮助出别人!

    关于javascript - 拖放 API 和表格行(仅限 vanilla JS),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62138942/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com