Build a Bootstrap 4 Responsive TODO Crud List Web App in Browser Using HTML5 & Javascript Full Project For Beginners

 

 

 

index.html

 

 

<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8" />
    <title></title>
    <link rel="stylesheet" href="main.css">
    <link href="https://fonts.googleapis.com/css?family=Orbitron:400,700" rel="stylesheet">
</head>
<body>
    <header>
        <img src="http://imghst.co/99/1qwH=Cit&7.jpg" alt="clocks">
    </header>

    <div class="main">
        <div class="badge">CLOCKER</div>

        <div class="clock">
            <span id="hour">02:</span>
            <span id="minute">30:</span>
            <span id="second">23</span>
        </div>
    </div>

    <script src="app.js" charset="utf-8"></script>
</body>
</html>



* {
    padding: 0;
    margin: 0;
    box-sizing: border-box;
}

header {
    position: relative;
}

header {
    position: relative;
}

header img {
    z-index: 1;
    width: 100%;
}


.main {
    /* Positions over the Image completely centered */
    z-index: 2;
    text-align: center;
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    /* Additional Settings */
    color: black;
    background: #ccc;
    height: 200px;
    border: 25px solid gray;
    padding: 0px 7px 28px 7px;
    border-radius: 15px;
    width: 455px;
}

.badge {
    position: relative;
    top: -40px;
    display: inline-block;
    color: white;
    padding: 5px;
    background: red;
    font-weight: bold;
    border-radius: 7px;
}

.clock {
    font-family: 'Orbitron', sans-serif;
    color: darkgreen;
}

span {
    font-size: 4.25rem;
}


@media (max-width: 900px) {
    .main {
        height: 175px;
        border: 25px solid gray;
        padding: 0px 7px;
        border-radius: 15px;
        width: 405px;
    }

    span {
        font-size: 3.25rem;
    }


}

@media (max-width: 740px) {
    .main {
        height: 150px;
        width: 295px;
    }

    span {
        font-size: 2.25rem;
    }
}

@media (max-width: 555px) {
    .main {
        height: 135px;
        width: 250px;
    }

    span {
        font-size: 1.5rem;
    }
}

@media (max-width: 425px) {
    .main {
        height: 125px;
        width: 210px;
    }

    span {
        font-size: 1rem;
    }
}

@media (max-width: 320px) {
    .main {
        width: 180px;
    }

    span {
        font-size: 0.8rem;
    }
}



'use strict';
// TODO ======================================
const Todo = function(text) {
  this.text = text;
  this.id = null;
  this.done = false;
}

Todo.prototype.getText = function() {
  return this.text;
}

Todo.prototype.setText = function(textToSet){
  this.text = textToSet;
}

Todo.prototype.setID = function(id) {
  this.id = id;
}

Todo.prototype.getID = function(id) {
  return this.id;
}

Todo.prototype.isDone = function(){
  return this.done;
}

Todo.prototype.changeState = function(){
  if(this.isDone()){
    this.done = false;
  }else {
    this.done = true;
  }
}

Todo.prototype.editText = function(newText){
  this.setText(newText);
}

// LIST ======================================

const List = function(){
  this.items = [];
  this.idsUsed = []; // stores all Todo's id
}

List.prototype.generateID = function(){
  const range = this.idsUsed.length + 1; 
  let unique = false;
  let randomID = -1;
  do {
    //ids will be generated in correct order because the range is very narrow and each id has to be unique
    randomID = Math.floor((Math.random() * range) + 1);
  } while(this.idsUsed.indexOf(randomID) !== -1)
  return randomID;
}

List.prototype.addTodo = function(text) {
  const todoObj = new Todo(text);
  const randID = this.generateID();
  todoObj.setID(randID);
  this.items.push(todoObj);
  this.idsUsed.push(randID);
  return todoObj;
}

//returns Todo object with specified id
List.prototype.findTodoById = function(id){
  const todo = this.items.filter(function(todo){
    if(todo.id === id) {
      return true;
    }
  })[0];
    return todo;
}

// returns a positon of todo in collection of items 
List.prototype.getItemCurrentIndex = function(id) {
  let index = -1;
  const todo = this.items.filter(function(todo, x){
    if(todo.id === id) {
      index = x;
      return true;
    }
  });
  return index;
}

// removes todo from items collection
List.prototype.removeTodo = function(id) {
  const index = this.getItemCurrentIndex(id);
  this.items.splice(index,1);
  return true;
}


List.prototype.changeItemState = function(id) {
  const todo = this.findTodoById(id);
  todo.changeState();
}



// LIST MANAGER ======================================
function ListManager() {
  this.list = new List();
  this.inputForm= document.querySelector('#addItemForm');
  this.inputField = document.querySelector('.input-field');
  this.listWrapper = document.querySelector('main');
  this.todoListEl = document.querySelector('.listItemsToDo');
  this.doneListEl = document.querySelector('.listItemsDone');
  this.buttons = {
    "remove": "btnRemove",
    "done": "btnDone",
    "undo": "btnUndo"
  }
};

ListManager.prototype.addItemToList = function(text) {
  const todoObj = this.list.addTodo(text);
  return todoObj;
}

ListManager.prototype.removeItemFromList = function(id) {
  this.list.removeTodo(id);
  this.renderList();
  return true;
}

ListManager.prototype.changeItemState = function(id) {
  this.list.changeItemState(id);
  this.renderList();
  return true;
}

ListManager.prototype.listenForInput = function() {
  const self = this;
  this.inputForm.addEventListener('submit',function(e){
    e.preventDefault();
    let userInput = self.inputField.value;
    self.list.addTodo(userInput);
    self.inputField.value = '';
    console.log('new todo added!');
    self.renderList();
  });
}

ListManager.prototype.renderList = function(){
  const items = this.list.items;
  this.todoListEl.innerHTML = '';
  this.doneListEl.innerHTML = '';
  for(let i = 0; i < items.length; i++) {
    const markup = this.prepareListItemHTML(items[i]);
    this.appendItemToList(this.createLI(markup), items[i].isDone());
  }
  
  if(this.todoListEl.childElementCount === 0) {
    this.todoListEl.innerHTML = '<span class="default text-muted">Woohoo! No todos!</span>';
  }
  
  if(this.doneListEl.childElementCount === 0) {
    this.doneListEl.innerHTML = '<span class="default text-muted">Nothing here yet</span>';
  }
};

ListManager.prototype.prepareListItemHTML = function(todoObj) {
  let listItemHTML = '';
  listItemHTML += '<input class="todoID" type="hidden" value="' +todoObj.getID()+ '">';
  listItemHTML += todoObj.getText();
  listItemHTML += '<div class="buttons">';
  if(!todoObj.isDone()) {
      listItemHTML += '<button class="' +this.buttons["remove"]+ '"></button>';
      listItemHTML += '<button class="' +this.buttons["done"]+ '"></button>';
    } else {
      listItemHTML += '<button class="' +this.buttons["undo"]+ '"></button>';
    }
    listItemHTML += '</div>';
  return listItemHTML;
}

ListManager.prototype.createLI = function(contentHTML) {
  const li = document.createElement('LI');
  li.innerHTML = contentHTML;
  return li;
}

ListManager.prototype.appendItemToList = function(li, status) {
  if(!status) {
    this.todoListEl.appendChild(li);
  } else {
    this.doneListEl.appendChild(li);
  }
  
  return true;
};
  
  ListManager.prototype.listenForAction = function(){
    const self = this;
    self.listWrapper.addEventListener('click', function(e){
      // check if button is clicked
      if(e.target.tagName === 'BUTTON') {
        // get id value of the <li> element path: button(target)->buttons div(parentNode) - input (prevSibling) - value = item's id
        const itemID = Number(e.target.parentNode.previousElementSibling.value);
        const listItem = e.target.parentNode.parentNode;
        //distinguish which button is clicked and assign proper action to it
        if(e.target.className === 'btnRemove') {
          self.removeItemFromList(itemID);
        }
        
        if(e.target.className === 'btnDone' || e.target.className === 'btnUndo') {
          self.changeItemState(itemID);
        }
      }
    });
  };


  
ListManager.prototype.manage = function() {
  this.renderList();
  this.listenForInput();
  this.listenForAction();
}

const lm = new ListManager();
lm.manage();

Leave a Reply