简体   繁体   English

从2D数组创建可编辑的HTML表

[英]Creating an editable HTML table from 2D array

So I am trying to make a flashcards website, where users can add, edit, and delete flashcards. 所以我想制作一个flashcards网站,用户可以在其中添加,编辑和删除抽认卡。 There are two cards - front and back. 有两张牌 - 正面和背面。 The user can already add words, but cannot edit or delete them. 用户可以添加单词,但无法编辑或删除它们。 For the purposes of this question I will use an example array: 出于这个问题的目的,我将使用一个示例数组:

var flashcards = [["Uomo", "Man"],["Donna", "Woman"],["Ragazzo", "Boy"]]

But I would like a more user-friendly way to edit the flashcards, preferably using a table like this: 但是我想要一种更加用户友好的方式来编辑抽认卡,最好使用这样的表格:

<table>
  <tr>
    <th>Front</th>
    <th>Back</th> 
  </tr>
  <tr>
    <td><input type="text" name="flashcard" value="Uomo"> </td>
    <td><input type="text" name="flashcard" value="Man"></td>
  </tr>
    <tr>
    <td><input type="text" name="flashcard" value="Donna"></td>
    <td><input type="text" name="flashcard" value="Woman"></td>
  </tr>
      <tr>
    <td><input type="text" name="flashcard" value="Ragazzo"></td>
    <td><input type="text" name="flashcard" value="Boy"></td>
  </tr>
</table>

<button type="button">Add more</button>
<br>
<button type="button">Save changes</button>

So they can update their flashcards editing the input fields, or clicking "add more" and it creating a new row. 因此,他们可以更新编辑输入字段的抽认卡,或单击“添加更多”并创建新行。 Clicking "save changes" updates the array to the content of the table. 单击“保存更改”会将数组更新为表的内容。

I don't mind it not being a HTML table per se, but something that is easy to edit for the user. 我不介意它本身不是HTML表,而是易于为用户编辑的东西。

I just cannot figure out the best way to approach this. 我无法弄清楚解决这个问题的最佳方法。 Any advice? 有什么建议?

我认为你可以使用就地编辑系统,我找到了一个很好的教程创建一个就地编辑系统

I already recommended VueJS - it really is a pretty good tool for this problem. 我已经推荐了VueJS - 它确实是解决这个问题的好工具。 Regardless, I have typed up a basic solution using vanilla JavaScript. 无论如何,我使用vanilla JavaScript键入了一个基本解决方案。 For the editing part it uses the contenteditable HTML attribute which allows the end-user to double click an element and change it's textContent. 对于编辑部分,它使用contenteditable HTML属性,允许最终用户双击一个元素并更改它的textContent。 The html display is basic so you can change it however to fit your needs html显示是基本的,因此您可以根据自己的需要进行更改

<div id=style="width: 100%;">
  <ul id="table" style="list-style-type: none; display: inline-block;">

  </ul>
</div>
<script>
var flashcards = [["Uomo", "Man"],["Donna", "Woman"],["Ragazzo", "Boy"]];
var displayedCard = []; //Using a parallel array to keep track of which side is shown
for(var i = 0; i < flashcards.length; i++){
    displayedCard.push(0);
}
function renderFlashcardTable(){ //This will do the initial rendering of the table
    let ulTable = document.getElementById("table");
    for(var i = 0; i < flashcards.length; i++){
        let card = flashcards[i];
        let indexOfSideShown = displayedCard[i];
        let li = document.createElement("li");
        let cardValueSpan = document.createElement("span");
        cardValueSpan.innerHTML = card[indexOfSideShown]; //Get the value of the side of the card that is shown
        cardValueSpan.setAttribute("contenteditable", "true"); 
        cardValueSpan.oninput = function(e){ //This method gets called when the user de-selects the element they have been editing
            let li = this.parentElement;
            let sideIndex = parseInt(li.getAttribute("side-index"));
            card[sideIndex] = this.textContent;
        }
        li.appendChild(cardValueSpan);
        li.appendChild(getFlipSidesButton(li));
        li.setAttribute("side-index", indexOfSideShown);
        li.setAttribute("card-index", i);
        ulTable.appendChild(li);
    }
}
function getFlipSidesButton(listItem){//This is generated for each card and when clicked it "flips the switch"
    let btn = document.createElement("button");
    btn.innerHTML = "Flip card";
    btn.onclick = function(e){
        let card = flashcards[listItem.getAttribute("card-index")];
        let index = parseInt(listItem.getAttribute("side-index"));
        let nextSide = (index == 1) ? 0 : 1;
        listItem.setAttribute("side-index", nextSide);
        listItem.children[0].innerHTML = card[nextSide];
    }
    return btn;
}

renderFlashcardTable();
</script>

I've put together a working sample using pure native javascript with a data-driven approach. 我已经使用纯本机javascript和数据驱动方法组合了一个工作示例。 You can have a look and understand the way how data should be manipulated and worked with in large Js application. 您可以查看并了解在大型Js应用程序中如何操作和使用数据的方式。

The point here is to isolate the data and logic as much as possible. 这里的要点是尽可能地隔离数据和逻辑。

Hope this help. 希望这有帮助。

Codepen: https://codepen.io/DieByMacro/pen/rgQBPZ Codepen: https ://codepen.io/DieByMacro/pen/rgQBPZ

 (function() { /** * Default value for Front and Back */ const DEFAULT = { front: '', back: '', } /** * Class Card: using for holding value of front and back. * As well as having `update` method to handle new value * from input itself. */ class Card { constructor({front, back, id} = {}) { this.front = front || DEFAULT.front; this.back = back || DEFAULT.back; this.id = id; } update = (side, value) => this[side] = value; } /** * Table Class: handle rendering data and update new value * according to the instance of Card. */ class Table { constructor() { this.init(); } /** Render basic table and heading of table */ init = () => { const table = document.querySelector('#table'); const thead = document.createElement('tr'); const theadContent = this.renderRow('th', thead, { front: 'Front', back: 'Back' }) const tbody = document.createElement('tbody'); table.appendChild(theadContent); table.appendChild(tbody); } /** Handling add event from Clicking on Add button * Note the `update: updateFnc` line, this means we will refer * `.update()` method of Card instance with `updateFnc()`, this is * used for update value Card instance itself. */ add = ({front, back, id, update: updateFnc }) => { const tbody = document.querySelector('#table tbody'); const row = document.createElement('tr'); const rowWithInput = this.renderRow('td', row, {front, back, id, updateFnc}); tbody.appendChild(rowWithInput); } renderInput = (side, id, fnc) => { const input = document.createElement('input'); input.setAttribute('type','text'); input.setAttribute('name',`${side}-value-${id}`) input.addEventListener('change', e => this.onInputChangeHandler(e, side, fnc)); return input; } renderRow = ( tag, parent, { front, back, id, updateFnc }) => { const frontColumn = document.createElement( tag ); const backColumn = document.createElement( tag ); /** Conditionally rendering based on `tag` type */ if ( tag === 'th') { frontColumn.innerText = front; backColumn.innerText = back; }else { /** Create two new inputs for each Card instance. Each handle * each side (front, back) */ const inputFront = this.renderInput('front', id, updateFnc); const inputBack = this.renderInput('back', id, updateFnc); frontColumn.appendChild(inputFront); backColumn.appendChild(inputBack); } parent.appendChild(frontColumn) parent.appendChild(backColumn) return parent; } /** Getting new value and run `.update()` method of Card, now referred as `fnc` */ onInputChangeHandler = (event, side, fnc) => { fnc(side, event.target.value); } } class App { /** * Holding cards data * Notice this is an object, not an array * Working with react for a while, I see most of the times data as an object works best when it comes to cRUD, this means we don't have to iterate through the array to find the specific element/item to do the work. This saves a lot of time */ cards = {}; constructor(){ this.domTable = new Table(); this.domAdd = document.querySelector('#btn-add'); this.domResult = document.querySelector('#btn-result'); this.domAdd.addEventListener('click', this.onClickAddHandler ); this.domResult.addEventListener('click', this.onClickResultHandler ); } onClickAddHandler = () => { const id = uuid(); const newCard = new Card({id}); this.cards[id] = newCard; this.domTable.add(newCard) } onClickResultHandler = () => { /** * Using `for ... in ` with object. Or you can use 3rd party like lodash for iteration */ for (const id in this.cards) { console.log({ front: this.cards[id].front, back: this.cards[id].back, id: this.cards[id].id }); } }; } // Start the application const app = new App(); })(); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/node-uuid/1.4.8/uuid.min.js"></script> <div id="table"></div> <button id="btn-add">Add</button> <button id="btn-result">Result</button> 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM