首页 > 解决方案 > 在 vanilla JS 中的事件处理和 DOM 附加表单时遇到问题

问题描述

我正在使用 vanilla JS、CSS、HTML 创建一个简单的抽认卡应用程序。作为一个 JS 菜鸟,当我尝试做基本的 CRUD 时(我获取到 Rails API 以实现持久性),我的 DOM 操作被踢了。

我正在使用 console.log 和调试器绕圈子,试图了解我的应用程序的流程。这些问题在没有刷新浏览器的情况下出现。创建时,会出现 3 张额外的“fantom”卡。编辑也有问题,它还会创建一张新卡。我也在尝试维护一个allFlashcard数组。我的困惑是a)如何使用一个函数来呈现新的或更新的卡片,b)如何使用相同的表单进行创建和编辑。

编码 -

HTML:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link rel="stylesheet" href="src/style.css">

    <!-- Compiled and minified CSS -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.css" />

    <title>CrashCard</title>
  </head>

    <body>
      <!-- header -->
      <header>
        <h1>Crash Card</h1>
        <button id="modal-btn" class="button">Create Card</button>
      </header>

      <!-- modal -->
      <div id="my-modal" class="modal">
        <div class="modal-content">
          <div class="modal-header">
            <span class="close">&times;</span>


            <!-- form  -->
            <form class="ui form" id="form">
              <div class="field">
                <label>Card Front</label>
                <input type="text" name="card-front" placeholder="Card Front" id="card-front" value="">
            </div>
              <div class="field">
                <label>Card Back</label>
                <input type="text" name="card-back" placeholder="Card Back" id="card-back" value="">
              </div>
              <div class="field">
              </div>
              <button class="ui button" id="submit" type="submit">Create</button>
              </form>

            </div>
        </div>
      </div>


      <!-- card -->
      <div id="card-container">
      </div>



      <script type="text/javascript" src="src/script.js"></script>
    </body>
</html> 

CSS:

.container {
width: 200px;
height: 260px;
margin: 5px;
padding: 3px;
perspective: 800px;
display: inline-flex;
}

:root {
  --modal-duration: 1s;
  --modal-color: #b4bdc5;
}


/* body {
  font-family: Arial, Helvetica, sans-serif;
  background: #f4f4f4;
  font-size: 17px;
  line-height: 1.6;
  display: flex;
  height: 100vh;
  align-items: center;
  justify-content: center;
} */

.button {
  background: #428bca;
  padding: 1em 2em;
  color: #fff;
  border: 0;
  border-radius: 5px;
  cursor: pointer;
}

.button:hover {
  background: #3876ac;
}

.modal {
  display: none;
  position: fixed;
  z-index: 1;
  left: 0;
  top: 0;
  height: 100%;
  width: 100%;
  overflow: auto;
  background-color: rgba(0, 0, 0, 0.5);
}

.modal-content {
  margin: 10% auto;
  width: 60%;
  box-shadow: 0 5px 8px 0 rgba(0, 0, 0, 0.2), 0 7px 20px 0 rgba(0, 0, 0, 0.17);
  animation-name: modalopen;
  animation-duration: var(--modal-duration);
}

.modal-header h2,
.modal-footer h3 {
  margin: 0;
}

.modal-header {
  background: var(--modal-color);
  padding: 15px;
  color: #fff;
  border-top-left-radius: 5px;
  border-top-right-radius: 5px;
}

.modal-body {
  padding: 10px 20px;
  background: #fff;
}

.modal-footer {
  background: var(--modal-color);
  padding: 10px;
  color: #fff;
  text-align: center;
  border-bottom-left-radius: 5px;
  border-bottom-right-radius: 5px;
}

.close {
  color: #ccc;
  float: right;
  font-size: 30px;
  color: #fff;
}

.close:hover,
.close:focus {
  color: #000;
  text-decoration: none;
  cursor: pointer;
}

@keyframes modalopen {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}


header {
display: flex;
align-items: center;
justify-content: center;
align-content: center;
text-align: center; 
}





/* .sticky {
 position: sticky;
 top: 0;
 background-color: white;
 display: block;
 z-index: 1;
} */

/* .parallax { */
 /* The image used */
 /* background-image: url("https://images.unsplash.com/photo-1556745750-68295fefafc5?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=967&q=80 */
/* https://images.unsplash.com/photo-1556745750-68295fefafc5?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=967&q=80 */
/* 

/* Set a specific height */
 /* min-height: 300px; */
 /* Create the parallax scrolling effect */
 /* background-attachment: fixed;
 background-position: center;
 background-repeat: no-repeat;
 background-size:auto; */
/* } */

#card-container {
  margin-top: 2em; 
  padding: 5%; 
 height: 100vh;
 overflow-y: scoll;
}

.card {
justify-items: center; 
padding: 2px; 
width: 100%;
height: 100%;
transform-style: preserve-3d;
transition: transform 1s;
color: white;
position: relative;
}

.card h2 {

text-align: center; 
margin: 0;
display: block;
width: 100%;
height: 100%;
backface-visibility: hidden;
position: absolute;
}

.card h4 {
text-align: center; 
position: relative;
margin: 0;
display: block;
width: 100%;
height: 100%;
backface-visibility: hidden;
position: absolute;
overflow-y: scroll ;
}

.card .front {
display: flex;
align-items: center;
justify-content: center;
background: black;
color: white;
text-align: center;
z-index: 1; 
}

.card .back {
background: rgb(241, 241, 248);
transform: rotateY( 180deg);
color: black;
text-align: center; 
z-index: 2;
display: 
}

.card.flipped {
transform: rotateY( 180deg);
}

Javascript:

document.addEventListener('DOMContentLoaded', () => {
    console.log("DOM is loaded");



    // set variables and DOM elements 

    let allFlashcards = [];
    const form = document.getElementById('form');
    const card = document.querySelector('#card-container');
    const modal = document.querySelector('#my-modal');
    const modalBtn = document.querySelector('#modal-btn');
    const closeBtn = document.querySelector('.close');


    // fetch all cards


    fetch('http://localhost:9000/api/v1/flashcards')
        .then(res => res.json())
        .then(flashcards => {
                allFlashcards = flashcards
                renderCards()
     });



    // render cards to DOM

    const renderCards = () => {
        console.log("in rendercard")
        return allFlashcards.map(c => {
            return card.innerHTML += `
            <div class="container">
                <div class="card" data-card=${c.id}>
                    <i class="black delete icon" id="delete-card"></i>
                    <i class="black edit icon" id="edit-card"></i>
                    <h2 class="front" data-id=${c.id}>${c.body_front}</h>
                    <h4 class="back">${c.body_back}</h4>
                </div>
            </div>
           `
        })
    };

    // listen for card click and flip

    card.addEventListener('click', e => {
        if (e.target.dataset.id) {
            return e.target.parentNode.classList.toggle('flipped')
        } else if (Array.from(e.target.classList).includes('back')) {
            return e.target.parentNode.classList.toggle('flipped')
        }
    });

    // ****** MODAL SETUP ********

    // Modal Events

    modalBtn.addEventListener('click', openModal);
    closeBtn.addEventListener('click', closeModal);
    window.addEventListener('click', outsideClick);

    // Open
    function openModal() {
        modal.style.display = 'block';
        createCard()
    }

    // Close
    function closeModal() {
        modal.style.display = 'none';
    }

    // Close If Outside Click
    function outsideClick(e) {
        if (e.target == modal) {
            modal.style.display = 'none';
        }
    }

    // create new card
    const createCard = () => { 
        console.log("in create")
         form.addEventListener('submit', (e) => {
            e.preventDefault();
            debugger 
            console.log("I was cliked too")
            const cardFront = document.getElementById('card-front').value
            const cardBack = document.getElementById('card-back').value
            fetch('http://localhost:9000/api/v1/flashcards', {
                method: "POST",
                headers: {
                    "Content-Type": "application/json",
                    "Accept": "application/json"
                },
                body: JSON.stringify({
                    body_front: cardFront,
                    body_back: cardBack
                })
                })
                .then(res => res.json())
                .then(newCard => {
                    allFlashcards.push(newCard)
                    closeModal()
                })
         });
    };



    // delete cards

    card.addEventListener('click', e => {
        const clickedCard = e.target.parentElement
        const foundCard = allFlashcards.find(fcard => {
            return fcard.id == clickedCard.dataset.card
        })
        console.log(foundCard)
        if (e.target.id == "delete-card") {
            console.log(card.parentElement)
            card.lastElementChild.remove(clickedCard)
            confirm("Do you want to delete this card?")
            fetch(`http://localhost:9000/api/v1/flashcards/${foundCard.id}`, {
                method: "DELETE"
            })
                originalCardIndex = allFlashcards.indexOf(foundCard)
                allFlashcards.splice(originalCardIndex, 1)
                console.log("in Delete:", allFlashcards)
        };
    });


    // edit cards 
    card.addEventListener('click', e => {
        if (e.target.id == "edit-card") {
            openModal()
            editCard()
        }
     });

    const editCard = () => {
        console.log("in editcard")
        const cardFront = card.querySelector('.front').innerText
        const cardBack = card.querySelector('.back').innerText 
        const clickedCard = e.target.parentElement
        const foundCard = allFlashcards.find(fcard => {
            return fcard.id == clickedCard.dataset.card
        })
            document.getElementById('card-front').value = cardFront
            document.getElementById('card-back').value = cardBack
            let submitBtn = document.getElementById('submit')
            submitBtn.innerText = "Edit"
            form.addEventListener('submit', e => { 
             e.preventDefault()
            const editedCardFront = document.getElementById('card-front').value
            const editedCardBack = document.getElementById('card-back').value
            fetch(`http://localhost:9000/api/v1/flashcards/${foundCard.id}`, {
                method: "PATCH",
                headers: {
                    "Content-Type": "application/json",
                    "Accept": "application/json"
                },
                body: JSON.stringify({
                    body_front: editedCardFront,
                    body_back: editedCardBack
                })
            })
            .then(res => res.json())
            .then(editedCard => {
                originalCardIndex = allFlashcards.indexOf(foundCard)
                allFlashcards.splice(originalCardIndex, 1, editedCard)
                console.log("in Edit:", allFlashcards)
            });
        });
    };




}); // end DOMContentLoaded

总的来说,我喜欢一些关于如何正确配置的建议。

标签: javascriptformsfetchrender

解决方案


推荐阅读