to do app in html5

Posted by on February 26th, 2018  •  1 Comment

I spent a full day trying to debug an indexedDB JavaScript lesson. It would work on my iPhone, and on codepen, but nowhere else. It wouldn’t  work at all on my MacBook, except for codepen.com. I knew my code was clean because typing errors are always the first thing I check. And the way it was behaving, I could tell it was some kind of security blockage either on the hosting servers or in the browsers.

While searching for an answer I came across another free online tutorial, and this one worked. I don’t know why it worked because it was structured much the same, though with subtle differences. Anyway I was able to combine stuff I’d learned in both tutorials to make a pretty sweet little  pure  HTML5 and vanilla JavaScript  note taking app.

Once I had it running I realized it was missing the drag and drop functionality I’ve come to expect in apps like wunderlist. After a quick google search, I found a tutorial that built a drag and drop list. Sadly, that one has a problem on mobile devices. You can drag the list items on a computer, but not on a phone. Phones have a different set of responses and the  eventListeners are set up completely different from computers.

But more searching led me to this cool tutorial page with links out to the slip.js library.

And finally I had a very nice little “todo” note taker app. It’s still not hooked up to a database…that may be coming. But it’s actually quite useable. There are 400 lines of code, not counting the slip javascript library. I prettied it up with some style sheets, and tried to make it mobile friendly.

It’s tricky to embed it in WordPress. There are conflicts with classes and ID’s. So instead, here is a direct link on my server.. It remembers your list items as long as you don’t clear your browsers cache. It does not sync with your computer though… You need wunderlist for that.

Just for practice, I’ve also posted it up on codepen so that you can tinker with it, if you should so desire.

There are two js files that run it. This is the one that hooks it up to the indexedDB database.

var todoDB = (function(){
  var tDB = {};
  var datastore = null;

  //todo: add methods for interacting with db here
  //open connection to the datastore

  tDB.open = function(callback){
    //database version
    var version = 1;

    //open connection to db
    var request = indexedDB.open('todos', version);

    //handle upgrades to datastore
    request.onupgradeneeded = function(e) {
      var db = e.target.result;

      e.target.transaction.onerror = tDB.onerror;

      //delete old datastore
      if(db.objectStoreNames.contains('todo')) {
        db.deleteObjectStore('todo');
      }

      //create a new datastore
      var store = db.createObjectStore('todo', {
        keyPath: 'timestamp'
      });

    };//end request.onupgradeneeded function

    //handle success datastore access
    request.onsuccess = function(e) {
      //get a reference to the DB
      datastore = e.target.result;

      //execute callback
      callback();
    };
    //handlerrors opening datastore
    request.onerror = tDB.onerror;

  };//end tDB.open = function

  //fetch all the todo items in the datastore
  tDB.fetchTodos = function(callback) {
    var db = datastore;
    var transaction = db.transaction(['todo'], 'readwrite');
    var objStore = transaction.objectStore('todo');

    var keyRange = IDBKeyRange.lowerBound(0);
    var cursorRequest = objStore.openCursor(keyRange);

    var todos = [];

    transaction.oncomplete = function(e) {
      //execute callback
      callback(todos);
    };

    cursorRequest.onsuccess = function(e){
      var result = e.target.result;

      if(!!result == false) {
        return;
      }

      todos.push(result.value);

      result.continue();
    };

    cursorRequest.onerror = tDB.onerror;
  };

  /**
  * create new todo item
  */
  tDB.createTodo = function(text, callback){
    // get reference to db
    var db = datastore;

    //intiate new transaction
    var transaction = db.transaction(['todo'], 'readwrite');

    //get the datastore
    var objStore = transaction.objectStore('todo');

    //create a timestamp for todo item
    var timestamp = new Date().getTime();

    //create an object for the todo itemsv
    var todo = {
      'text': text,
      'timestamp': timestamp
    };

    //create the datastore cursorRequest
    var request = objStore.put(todo);

    //handle the success put
    request.onsuccess = function(e) {
      //execute callback
      callback(todo);
    };

    //handle handlerrors
    request.onerror = tDB.onerror;
  };//end tDB.createTodo function

  /**
  * delete a todo item
  */
  tDB.deleteTodo = function(id, callback){
    var db = datastore;
    var transaction = db.transaction(['todo'], 'readwrite');
    var objStore = transaction.objectStore('todo');

    var request = objStore.delete(id);

    request.onsuccess = function(e) {
      callback();
    }
    //writes errors to the console
    request.onerror = function(e){
      console.log(e);
    }
  };//end tDB.deleteTodo function
  //export the tDB object
  return tDB;
}());

And here is the one where I did quite a bit of customization, you can see the drag and drop “slip” references at the bottom.

window.onload = function(){
  //todo: app code goes here
  //display todo items by passing in the refreshTodos function as a parameter?
  todoDB.open(refreshTodos);

  //get references to html elements
  var newTodoForm = document.getElementById('new-todo-form');
  var newTodoInput = document.getElementById('new-todo');
  var submitBtn = document.querySelector('.submitBtn');
  //my form submitter addEventListener
  newTodoForm.addEventListener('submit', triggerForm);

  submitBtn.addEventListener('click', triggerForm);

  //handle the form sumissions
function triggerForm() {
    //get text
    var text = newTodoInput.value;

    //check tomake sure test is not blank or just spaces
    if(text.replace(/ /g,'') != ''){
      //create the todo item
      todoDB.createTodo(text, function(todo) {
        refreshTodos();
      });
    }

    //reset input field
    newTodoInput.value = '';

    //don't send the form
    return false;
  };//end triggerForm function

  //update the list of todo items.
  function refreshTodos(){
    todoDB.fetchTodos(function(todos){
      var todoList = document.getElementById('todo-items');
      todoList.innerHTML = '';

      for(var i = 0; i < todos.length; i++){
        //read the todo items backwards-most recent first
        var todo = todos[(todos.length - 1 - i)];

        var li = document.createElement('li');
        li.id = 'todo-' + todo.timestamp;
        //li.setAttribute('draggable', 'true');
        var checkbox = document.createElement('button');
        checkbox.textContent = 'X';
        checkbox.className = 'todo-checkbox';//""
        checkbox.setAttribute('data-id', todo.timestamp);//""



        var span = document.createElement('span');
        span.innerHTML = todo.text;

        li.appendChild(span);
        li.appendChild(checkbox);
        todoList.appendChild(li);

        //set up listener checkbox
        checkbox.addEventListener('click', function(e){
          var id = parseInt(e.target.getAttribute('data-id'));

          todoDB.deleteTodo(id, refreshTodos);
        });
      }//end for i < todos.length
//my non mobile dragger function used to be here.

    });//end todoDB.fetchTodos
    newTodoInput.focus();

  }//end function refreshTodos
//begin slippery functions
//slideThem = <ul id="todo-items">
var slideThem = document.querySelector('#todo-items');
new Slip(slideThem);//apply slip library to <ul>
slideThem.addEventListener('slip:reorder', function(e){
  e.target.parentNode.insertBefore(e.target, e.detail.insertBefore);
});
//end slipper function
};//end window.onload

The style sheet got fairly long  because…well…I’m sort of artsy and I wanted it to be pretty.

* {
  -moz-box-sizing: border-box;
  -webkit-box-sizing: border-box;
  box-sizing: border-box;
}

body, html {
  padding: 0;
  margin: 0;
}

body {

  color: #545454;
  background: #f7f7f7;
}
#page-wrapper {
    font: 2em Verdana, Helvetica, sans-serif;
  width: 100%;
  max-width: 750px;
  margin: 0.2em auto;
  background: #fff;
  box-shadow: 0 1px 3px rgba(0,0,0,0.2);
  border-radius: 12px;
}
#page-wrapper footer {
border-top: 2px solid #0088cc;
padding: 0.5em;
background-color: rgb(207, 240, 245);
border-bottom-left-radius: 12px;
border-bottom-right-radius: 12px;
}
#page-wrapper footer h3 {
  font-size: 1.2em;
  color: #61bfee;
  margin:0;
  padding: 0;
  text-align: center;
}
#new-todo-form {
  padding: 0.5em;
  background: #0088cc;
  border-top-left-radius: 12px;
  border-top-right-radius: 12px;
}

#new-todo {
  width: 100%;
  padding: 0.5em;
  font-size: 1em;
  border-radius: 3px;
  border: 0;
}

#todo-items {
  list-style: none;
  padding: 0.3em 0.3em;
  margin: 0;
}

#todo-items li {
  margin: 0.5em 0 0 0;
  padding: 0;
  background-color: rgb(246, 232, 161);
  border: 1px solid rgb(153, 129, 67);
  display: flex;
  justify-content: space-between;
/* cursor: move; */
}
#todo-items li.dragElem {
  opacity: 0.6;
}
#todo-items li.over {
  border-top: 4px solid red;
}
#todo-items li span {
  margin: 0.7em 0.2em;

}
#page-wrapper button:hover {
  background-color: #0088cc;
  color: white;
}
.submitBtn {
      color: white;
      font-size: 1em;
      padding: 0.5em;
      margin: 0.5em 0 0 0;
      background-color: rgb(255, 162, 4);
}

.todo-checkbox {
  color: white;
  background-color: rgb(213, 119, 21);
  font-size: 1.2em;
  padding: 0.5em;
}

 

1 thought on “to do app in html5”

  1. Mark Webster says:

    this is lame, I’m commenting on my own website. Testing to see if this works.

Leave a Reply

Your email address will not be published. Required fields are marked *