Reliable JavaScript Certification Online #

Web browsers have APIs that allow you to store web apps data locally on users’ computers. It makes possible to store user preferences, user state, share data between pages, etc.
Storage is separated by origins, that means that pages can read data only from the same origin and do not have access to data from other origins. The same origin means the same protocol (eg. http), hostname (eg. www.example.org) and port (eg. 80).
You can specify the lifetime of stored data. It may be stored temporarily until the browser is closed, a certain amount of time, or permanently.
There are several client-side storage APIs you may use: web storage (local storage, session storage), cookies, indexedDB.

 

Local Storage

The Window object has a localStorage property, that refers to a Storage object. The Storage object is like associative array: stores key / value pairs.
Data stored in localStorage is persistent. It is not removed when the user closes the window or there is no defined expiration time.
Access to the localStorage is possible for the same origin. It means that documents that have the same protocol, hostname and port, share the same localStorage. They can read and overwrite data stored in it. Documents with different origins do not have access to each other localStorage. The localStorage is also associated with browser. The browser has no access to data stored in localStorage by another browser.

To store data in localStorage specify property name and assign value to it:

localStorage['name'] = 'Graham'; 
localStorage.surname = 'Smith';

You can retrieve data from localStorage in the following way:

var name =  localStorage.name;
var surname = localStorage['surname'];

There are also methods that you can use to access localStorage:

localStorage.setItem('city', 'Dublin'); // store a item city
localStorage.setItem('street', 'O'Connell St'); // store a item street
var city = localStorage.getItem('city'); // get value of the item city
var propertyName = localStorage.key(0); // get the name of a stored item with index 0:
localStorage.removeItem('street'); // delete the item street
localStorage.clear(); // delete all items

 

Session Storage

The Window object has a sessionStorage property. It is similar to the localStorage property and also refers to the Storage object. The differences between them are in lifetime and scope.
Data in the sessionStorage is valid as long as a page session lasts. The session expires when browser window or tab is closed. Then data in the sessionStorage is removed.
The scope of the sessionStorage is similar to the scope of localStorage and it is separated by the origin. The difference is that the sessionStorage is also scoped by the window. It means that two pages from the same origin opened in separate windows or tabs have separate sessionStorage data and do not have access to each other.

The sessionStorage API is the same as the localStorage:

sessionStorage.setItem('product', 'microwave');
var product = sessionStorage.product;
sessionStorage.removeItem('product');

 

Cookies

Cookies are small pieces of data related to a particular website and stored on user’s computers by the web browser. Cookies were designed so that they can be read and written by server-side scripts, stored on client-side and transmitted between browser and web server. Their purpose was to store the user’s state and activity. They can be used to remember information entered by the user into form fields (name, password, account settings, preferences etc.).

The API of cookies is quite old and there are no methods to access them in a simple way. Instead of this all cookies and their attributes are stored as one formatted string in the cookie property of the Document object.
The document.cookie is not a data property but an accessor property. It uses setter and getter functions and what you write is not the same as what you read.

Cookies are stored as key/value pairs.
To set a cookie simply assign to the cookie property a string of form key=value.

document.cookie = 'company=Infomaster';

In the same way you will assign more cookies:

document.cookie = 'departament=Accounting';
document.cookie = 'team=Taxes';

Cookies cannot contain whitespace, semicolons and commas. If your cookie contains these characters, you must encode it using the encodeURIComponent() global function:

document.cookie = 'system=' + encodeURIComponent('Business Intelligence');

When you read the cookie property you will receive a string that contains list of all cookies related to the current website. These cookies (key=value pairs) are separated by semicolon and space.
var allCookies = document.cookie; // returns a string: ‘company=Infomaster; departament=Accounting; team=Taxes; system=Business%20Intelligence’

In order to read a specified cookie value first you have to split the string returned by document.cookie into key=value pairs. Then for each returned chunk you have to extract the name and value of the cookie. If value was encoded you have to decode it accordingly, for example with the decodeURIComponent() function.

var allCookies = document.cookie;
var chunks = allCookies.split('; ');
var cookies = [];
for (var i = 0; i < chunks.length; i++) {
    var chunk = chunks[i];
    var keyValue = chunk.split('=');
    var name = keyValue[0];
    var value = decodeURIComponent(keyValue[1]);
    cookies[name] = value; 
}
var system = cookies['system']; // returns 'Business Intelligence'

In addition to a name and a value, cookie has optional attributes that set its lifetime and scope.
By default data stored in cookie is valid as long as the browser is open, and is lost when the browser is closed. Note that the cookie’s lifetime applies not to a single window, but to the entire web browser. If you wants to store a cookie longer than current browser session, specify a max-age attribute and inform the browser in seconds how long to keep the cookie. The browser then stores the cookie in a file and removes it when it expires. Another way is to set the expires attribute with a date in the GTM format.

Cookies are scoped by document origin and path. By default cookie is available for pages from the same directory as the page that created the cookie and for pages from subdirectories of that directory. For example if a cookie is created on the page www.example.org/product/product1.html it is also visible to www.example.org/product/product2.html and www.example.org/product/order/product3.html but it is not visible to www.example.org/contact.html. If you would like to change
the default behavior, specify a path for the cookie. The cookie with the path set to “/” is visible to any page from the same origin.
Because cookies are scoped by origin, a cookie created on www.example.org/index.html is not visible on the page www.shop.example.org/index.html. if you would like a cookie to be available on www.example.org and all subdomains, set the path to “/” and domain to www.example.org.

You may add secure attribute that means cookie can be transmitted only over secure https connection.

Additional cookie attributes you can specify when you set cookie value. Append these attributes to the cookie value and separate them with a semicolon:

document.cookie = "name=John; max-age=3600; path=/; domain=www.example.org; secure";

 

IndexedDB

IndexedDB is an object database for permanent data storage in the user’s browser. It is more powerful than Web storage and allows you to store larger amounts of structured data. IndexedDB uses indexes that enable effective data search. It allows you to create applications that store large amount of data and can work both online and offline.

IndexedDB is transactional database system. It is JavaScript object oriented database, where stored and retrieved objects are indexed with a key. IndexedDB databases are scoped by origin, only pages from the same domain can access a given database. Operations on IndexedDB are performed asynchronously and do not block applications.

API of IndexedDB

IndexedDB API is available through the indexedDB property of the Window object.

var indexedDB = window.indexedDB;

To create database use the open() method. The first parameter is the name of the database, the second is the version of it. The method returns IDBOpenDBRequest object.

var request = indexedDB.open("appDatabase", 3);

When you create a new database, the onupgradeneeded event will be triggered. In a handler of this event you can define or alter structure of the database.
The store object is created using the createObjectStore() method. It accepts as the first argument a name of the store and as the second argument an optional options object that refines the type of object. As option you may use keyPath property, that makes an object in the store unique.
Indexes are created using the createIndex() method that takes name of the index as the first argument and an options object that refines the type of index as the second argument.

request.onupgradeneeded = function(event) {
    // get db interface
    var db = event.target.result;
    // create an objectStore to hold information about users and define 
    var objectStore = db.createObjectStore("users", { keyPath: "id" });
    
    // create index to search users by name
    objectStore.createIndex("name", ["name.first", "name.last"], { unique: false });
    
    // create index to search users by city
    objectStore.createIndex("email", "email", { unique: true });
};

On the request object you should define handlers for success and error events.
In an success event handler you create transaction using the transaction() method. It takes the list of stores as the first parameter and type of transaction as the second parameter.
To add or update object in the store use the put() method, to retrieve object use the get() method, to remove element use the delete() method.
The index() method returns index object, that you can use to search data by index.

request.onsuccess = function(event) {
    db = event.target.result;
    var transaction = db.transaction(["users"], "readwrite");
    var usersStore = transaction.objectStore("users");

    // add data to usersStore
    usersStore.put({id: 1001, name: {first: "Marc", last: "Brown"}, email: "marc123@gmail.com"});
    usersStore.put({id: 1002, name: {first: "Tom", last: "Evans"}, email: "tom123@yahoo.com"});
    usersStore.put({id: 1003, name: {first: "Mike", last: "Wilson"}, email: "mike123@hotmail.com"});
    
    // Query the data
    var user1 = usersStore.get(1001);
    user1.onsuccess = function() {
        console.log(user1.result.name.first + ' ' + user1.result.name.last); // Marc Brown
    };
    
    // search data by index:
    var usersNameIndex = usersStore.index("name");
    var user2 = usersNameIndex.get(["Tom", "Evans"]);
    user2.onsuccess = function() {
        console.log(user2.result.name.first + ' ' + user2.result.email); // Tom tom123@yahoo.com
    };
    var usersEmailIndex = usersStore.index("email");
    var user3 = usersEmailIndex.get("mike123@hotmail.com");
    user3.onsuccess = function() {
        console.log(user3.result.name.first + ' ' + user3.result.email); // Mike mike123@hotmail.com
    };
    
    // Update data:
    var user1ToUpdate = usersStore.get(1001);
    user1ToUpdate.onsuccess = function(event) {
        var user1 = event.target.result;
        user1.email = "marc.xyz@gmail.com";
        usersStore.put(user1);
        
        var user1Test = usersStore.get(1001);
            user1Test.onsuccess = function() {
            console.log(user1Test.result.email); // marc.xyz@gmail.com
        };
    };
    
    //Delete data:
    var user1ToDelete = usersStore.delete(1002);
        user1ToDelete.onsuccess = function() {
        console.log("User with id=1002 deleted");
    };
 
};
request.onerror = function(event) {
    alert('IndexedDB database error: ' + event.target.errorCode);
};