Search code examples
javascriptformskeypresskeydownkeyup

Why does only keydown work, and not keyup or keypress?


I have the following codes of adding a todo into a todo app here:

HTML:

<div id="root">
  <form class="todoForm">
    <input class="input" placeholder="What do you need to do?" />
  </form>
  <div class="todos"></div>
  <div class="footer">
    <button class="All">All</button>
    <button class="All">Active</button>
    <button class="All">Completed</button>
  </div>
</div>

CSS:

.todoForm {
  margin-bottom: 20px;
}

.todos {
  margin-bottom: 20px;
}

.todo {
  margin-bottom: 10px;
}

.todoAndCheckBox {
  display: flex
}

.checkBox {
  border-radius: 50%;
  outline: green;
}


JS:

class displayTodos {
  constructor(root) {
    this.root = root;
    this.input = this.root.querySelector('.input');
    this.form = this.root.querySelector('.todoForm');
    // why does it work with keydown but not keyup
    this.form.addEventListener('keydown', this.submitForm);
    this.todos = this.root.querySelector('.todos');
    this.state = {
      todos: [
        {
          id: Math.random * 10000,
          text: 'Pie'
        },
        {
          id: Math.random * 10000,
          text: 'Soy milk'
        }
      ]
    }
    this.display();
  }
  
  submitForm = (e) => {
    if(e.key === 'Enter') {
      e.preventDefault();
      const typed = this.input.value;
      const newTodo = {
        id: Math.random * 10000,
        text: typed
      }
      
      const newTodos = [...this.state.todos, newTodo];
      
      this.state.todos = newTodos;
      this.display();
      this.input.value = ''
    }
  }
  
  display = () => {
    while(this.todos.firstChild) {
      this.todos.removeChild(this.todos.firstChild)
    }
    this.state.todos.forEach(todo => {
      const todoAndCheckBox = document.createElement('div');
      const todoDiv = document.createElement('div');
      todoAndCheckBox.classList.add('todoAndCheckBox');
      todoDiv.innerText = todo.text;
      const checkBox = document.createElement('input');
      checkBox.setAttribute('type', 'checkbox');
      checkBox.classList.add('checkBox');
      this.todos.appendChild(todoAndCheckBox);
      todoAndCheckBox.appendChild(checkBox);
      todoAndCheckBox.appendChild(todoDiv);
      
      todoAndCheckBox.classList.add('todo')
    })
  }
  
}

const root = document.querySelector('#root');
const instance = new displayTodos(root);

At this line:

this.form.addEventListener('keydown', this.submitForm);

I notice that if I changed keydown to keyup or keypress, when I press enter, the whole page will refresh even though I have e.preventDefault. My questions are:

  1. Why does the page/form refresh with the e.preventDefault?
  2. Why does only keydown work (meaning it stops the form from refreshing), but not keyup or keypress?

Solution

  • The submit form event is already being triggered by keydown, if you use e.preventDefault on keyup, it's too late. You should alter your code to bind to keydown and keyup. On keydown function you can call e.preventDefault(), and on the keyup function you can do your other logic.

    ALTERNATIVELY:

    you can disable form submits entirely with this code:

    <form onsubmit="return false;">
    

    and then manually trigger a form submit when you actually want it to happen using:

    document.forms[0].submit();
    

    above assumes you only have one form - you may want to use a css selector instead if you have multiple now or in future:

    document.querySelector("#formid").submit();
    

    Also note, submit buttons will no longer work, so you'd need to hook up events to their click events to submit via same code.