20
loading...
This website collects cookies to deliver better user experience
IndexedDB
API.to-do
application code on GitHub: https://github.com/Alilasteve/todo
defer
attribute. The defer keyword means our JavaScript will load after the HTML has finished loading. form
for submitting todos
for storage and ul
for showing todos
. todos
some padding to give room for double-clicking when we start deleting them.locaStorage
, and db.js for IndexedDB
. We shall switch their (HTML) script source depending on the storage we are working with.ul
, input
, and save button
. And hold them in variables as shown below:const todosUl = document.querySelector('.todos');
const input = document.querySelector('#add');
const saveBtn = document.querySelector('#save');
todos
such asconst todos = ['play piano', 'write some code', 'swim'];
DOMContentLoaded
to ensure the todos
are available to be shown when the page loads.window.addEventListener('DOMContentLoaded', () =>{
//show todos
todos.forEach( todo => {
let li = document.createElement('li');
li.textContent = todo;
todosUl.appendChild(li);
//delete todos
li.addEventListener('dblclick', () => {
todosUl.removeChild(li);
})
})
const addTodos = e => {
e.preventDefault();
let li = document.createElement('li');
li.textContent = input.value;
todos.push(input.value);
todosUl.appendChild(li);
input.value = '';
}
saveBtn.addEventListener('click', addTodos);
})
todos
, create a li
element and show each todo on the page. We can add new todos
to the array using the addTodos
function. todos
to the static array, but they disappear on page refresh. That is why we need alternative ways to store the todos
.LocalStorage
and SessionStorage
. The objects let us store data (in key/value pairs) and update it from the browser's storage. To view the data, open your browser. Right-click. Click Inspect => Application => Storage.LocalStorage
lets us store data as long as we want. SessionStorage
, by contrast, loses the data when we close the current window. localStorage.setItem('store_name', 'data_name');
//e.g
localStorage.setItem('name', 'Ahmed');
getItem
as shown:localStorage.getItem('store_name');
//e.g
localStorage.getItem('name'); //Ahmed
//or
localStorage // all stores
localStorage.removeItem('store_name');
//e.g
localStorage.removeItem('name');
localStorage.clear();
localStorage
stores data in strings. In our todo application, we use it as follows:const getTodos = () => {
let todos;
if(localStorage.getItem('todos') === null){
todos = [];
}else {
todos = JSON.parse(localStorage.getItem('todos'));
}
return todos;
}
todos.
Next, check if the browser already has a storage object called todos.
If it does not exist, we create an empty array. If it already exists, we can grab it in readiness to be used in other parts of the code.JSON.parse
to change the todos
from JSON to regular JavaScript literals. We then return the object to be used in other parts of the code.const saveTodos = inputData => {
const todos = getTodos();
todos.push(inputData);
localStorage.setItem('todos', JSON.stringify(todos));
}
saveTodos
, with inputData
parameter. We push the new data in the web storage. And recreate the localStorage
object. We stringify
the todos
before saving it because localStorage
stores data in strings. We then proceed and get the input value from the addTodos
function in the linesaveTodos(input.value);
const addTodos = e => {
e.preventDefault();
let li = document.createElement('li');
li.textContent = input.value;
saveTodos(input.value);
todosUl.appendChild(li);
input.value = '';
}
const deleteTodos = (todos, e) => {
const targetLi = todos.indexOf(e.target.textContent);
todos.splice(targetLi, 1);
localStorage.setItem('todos', JSON.stringify(todos));
}
localStorage.removeItem()
but use the array.splice
method because we are not sure the order we can decide to delete each element from the todos
store.li
we want to remove from localStorage
using the array.indexOf()
method. Add splice it from the array. Then, recreate the localStorage
object after the modification.localStorage
. That's where indexedDB
comes in.IndexedDB
, we can store various data types in many forms. It is a free database in the browser. The storage is not limited to strings as with localStorage
. idb
by Jake Archibald to use the promise API. In this simple demo, we shall use the IndexedDB
API, which uses events instead of promises. IndexedDB
works like a NoSQL database, such as MongoDB.database
name. Add object stores
. Object stores are like tables in MySQL or models in MongoDB.index
. In MongoDB, we would refer to them as schema
.transaction
that enables us to perform CRUD in the IndexedDB
. In the process, we use a cursor
to loop through records in the object store.let db;
db
using connectDB()
function.const connectIDB = () => {
const request = window.indexedDB.open('todos_db', 1);
request.addEventListener('upgradeneeded', e => {
db = e.target.result;
const objectStore = db.createObjectStore('todos_data', { keyPath: 'id', autoIncrement: true })
objectStore.createIndex('content', 'content', { unique: false });
})
request.addEventListener('error', e => {
console.log(e.target.errorCode);
})
request.addEventListener('success', () => {
db = request.result;
getTodos();
})
}
todos_db
database using window.indexedDB.open()
method.upgradeneeded
event. Or if we try to open the database with a higher version than the existing one. We create the name of the store. We have called it todos_data
. You can call it anything you want.autoIncrement
. Here, the keyPath
contains a unique id for each record in the database. We are using autoIncrement
to let IndexedDB
automatically increase it. Then, we tell IndexedDB
the actual data (name) each record should contain.db
variable. We can then display the result using getTodos()
function.todos
, let's create grab them from the form and save them.const addTodos = e => {
e.preventDefault();
const transaction = db.transaction(['todos_data'], 'readwrite');
const objectStore = transaction.objectStore('todos_data');
const newRecord = {content: input.value};
const request = objectStore.add(newRecord);
request.addEventListener('success', () => {
input.value = '';
})
transaction.addEventListener('complete', () => {
getTodos();
})
transaction.addEventListener('error', () => {
return;
})
}
e.preventDefault()
. readwrite
transaction in our formerly created todos_data
store. Next, we create a new create record and store it in the store.firstChild
of ul
to avoid record duplication. Next, we get the target store. cursor
method and success event, create a li
for every record using its unique id that we had saved earlier. li
. Remember to end the cursor
with cursor.continue()
to enable the code to proceed to the following li
creation and deletion.const getTodos = () => {
while(todosUl.firstChild){
todosUl.removeChild(todosUl.firstChild)
}
const objectStore = db.transaction('todos_data').objectStore('todos_data');
objectStore.openCursor().addEventListener('success', e => {
const cursor = e.target.result
if(cursor){
const list = document.createElement('li')
list.setAttribute('todo-id', cursor.value.id)
list.textContent = cursor.value.content
todosUl.appendChild(list)
list.ondblclick = deleteTodos
cursor.continue()
}
})
}
getTodos()
, we start a readwrite
transaction on the object store. Convert each todo
's id to a number and find its match with the one stored in the database. Then, delete it.const deleteTodos = e => {
const transaction = db.transaction(['todos_data'], 'readwrite')
const objectStore = transaction.objectStore('todos_data')
const todoId = Number(e.target.getAttribute('todo-id'))
objectStore.delete(todoId)
transaction.addEventListener('complete', () => {
if(!todosUl.firstChild){
const message = document.createElement('li')
message.textContent = 'No todo exist'
todosUl.appendChild(message)
}
e.target.parentNode.removeChild(e.target)
})
}
IndexedDB
. IndexedDB
as you would do in a typical server-side environment.IndexedDB
is that it can be complicated to use. 20