Skip to content Skip to sidebar Skip to footer

How to Load Java Script Again in Footer

Client-side storage

  • Previous
  • Overview: Client-side web APIs

Mod spider web browsers back up a number of ways for web sites to store data on the user's computer — with the user'south permission — so recall it when necessary. This lets yous persist information for long-term storage, save sites or documents for offline apply, retain user-specific settings for your site, and more. This article explains the very basics of how these work.

Customer-side storage?

Elsewhere in the MDN learning area we talked virtually the divergence betwixt static sites and dynamic sites. Nearly major modern web sites are dynamic — they store data on the server using some kind of database (server-side storage), so run server-side code to recollect needed information, insert it into static page templates, and serve the resulting HTML to the customer to exist displayed past the user's browser.

Client-side storage works on like principles, only has different uses. It consists of JavaScript APIs that allow yous to shop data on the customer (i.eastward. on the user's motorcar) and and so retrieve information technology when needed. This has many singled-out uses, such as:

  • Personalizing site preferences (e.g. showing a user's option of custom widgets, color scheme, or font size).
  • Persisting previous site activeness (eastward.chiliad. storing the contents of a shopping cart from a previous session, remembering if a user was previously logged in).
  • Saving data and avails locally so a site will be quicker (and potentially less expensive) to download, or be usable without a network connectedness.
  • Saving spider web application generated documents locally for use offline

Often client-side and server-side storage are used together. For example, you lot could download a batch of music files (perchance used by a spider web game or music actor application), store them within a client-side database, and play them equally needed. The user would but have to download the music files in one case — on subsequent visits they would be retrieved from the database instead.

Note: There are limits to the corporeality of data you can store using customer-side storage APIs (possibly both per individual API and cumulatively); the exact limit varies depending on the browser and possibly based on user settings. Meet Browser storage limits and eviction criteria for more information.

Old school: Cookies

The concept of client-side storage has been around for a long time. Since the early days of the spider web, sites have used cookies to shop information to personalize user experience on websites. They're the earliest form of customer-side storage unremarkably used on the web.

These days, there are easier mechanisms bachelor for storing customer-side data, therefore nosotros won't be teaching you how to use cookies in this commodity. All the same, this does not mean cookies are completely useless on the modern-day web — they are still used commonly to shop information related to user personalization and state, e.thou. session IDs and access tokens. For more data on cookies see our Using HTTP cookies article.

New school: Web Storage and IndexedDB

The "easier" features we mentioned above are as follows:

  • The Spider web Storage API provides a mechanism for storing and retrieving smaller, information items consisting of a proper name and a corresponding value. This is useful when you lot just need to store some simple data, like the user'south name, whether they are logged in, what colour to use for the background of the screen, etc.
  • The IndexedDB API provides the browser with a complete database organisation for storing complex information. This can exist used for things from complete sets of customer records to even circuitous data types like audio or video files.

You'll learn more than about these APIs below.

The Cache API

The Cache API is designed for storing HTTP responses to specific requests, and is very useful for doing things like storing website assets offline so the site can subsequently be used without a network connection. Cache is ordinarily used in combination with the Service Worker API, although information technology doesn't have to be.

Use of Enshroud and Service Workers is an advanced topic, and we won't be covering information technology in great item in this article, although nosotros will evidence an example in the Offline asset storage section below.

Storing simple data — web storage

The Web Storage API is very piece of cake to use — you shop simple name/value pairs of data (limited to strings, numbers, etc.) and recollect these values when needed.

Basic syntax

Let'south testify you how:

  1. Kickoff, go to our web storage blank template on GitHub (open up this in a new tab).
  2. Open up the JavaScript console of your browser's developer tools.
  3. All of your web storage information is contained within two object-like structures inside the browser: sessionStorage and localStorage. The commencement 1 persists data for every bit long as the browser is open (the information is lost when the browser is closed) and the 2nd i persists data fifty-fifty after the browser is closed and and so opened once again. Nosotros'll use the 2d one in this commodity as it is more often than not more useful. The Storage.setItem() method allows you to relieve a data particular in storage — it takes 2 parameters: the name of the item, and its value. Try typing this into your JavaScript panel (modify the value to your ain name, if y'all wish!):
                          localStorage.                        setItem                        (                        'name'                        ,                        'Chris'                        )                        ;                                          
  4. The Storage.getItem() method takes one parameter — the name of a data item y'all desire to retrieve — and returns the item's value. Now blazon these lines into your JavaScript console:
                                                  let                        myName                        =                        localStorage.                        getItem                        (                        'name'                        )                        ;                        myName                                          
    Upon typing in the second line, you should run into that the myName variable at present contains the value of the name data item.
  5. The Storage.removeItem() method takes one parameter — the proper name of a information item you want to remove — and removes that item out of spider web storage. Type the following lines into your JavaScript panel:
                          localStorage.                        removeItem                        (                        'name'                        )                        ;                        myName                        =                        localStorage.                        getItem                        (                        'proper noun'                        )                        ;                        myName                                          
    The 3rd line should at present return cypher — the name particular no longer exists in the web storage.

The data persists!

1 key characteristic of web storage is that the data persists betwixt page loads (and fifty-fifty when the browser is close downward, in the case of localStorage). Let's look at this in action.

  1. Open our spider web storage blank template once more, just this time in a different browser to the 1 y'all've got this tutorial open in! This will brand it easier to deal with.
  2. Blazon these lines into the browser's JavaScript console:
                          localStorage.                        setItem                        (                        'name'                        ,                        'Chris'                        )                        ;                        permit                        myName                        =                        localStorage.                        getItem                        (                        'name'                        )                        ;                        myName                                          
    You lot should see the proper name item returned.
  3. At present shut down the browser and open it up again.
  4. Enter the post-obit lines over again:
                                                  let                        myName                        =                        localStorage.                        getItem                        (                        'name'                        )                        ;                        myName                                          
    Yous should run across that the value is all the same available, even though the browser has been airtight and then opened again.

Split up storage for each domain

There is a separate data store for each domain (each carve up web address loaded in the browser). You volition see that if yous load two websites (say google.com and amazon.com) and try storing an particular on one website, it won't be available to the other website.

This makes sense — you can imagine the security issues that would arise if websites could see each other's data!

A more involved case

Allow's apply this new-found noesis past writing a working example to give you lot an thought of how spider web storage can be used. Our example will allow yous enter a name, afterward which the folio volition update to give yous a personalized greeting. This state will besides persist across page/browser reloads, because the name is stored in web storage.

You can notice the example HTML at personal-greeting.html — this contains a website with a header, content, and footer, and a form for entering your proper noun.

Let'southward build upwardly the example, so yous tin sympathize how it works.

  1. First, make a local copy of our personal-greeting.html file in a new directory on your computer.
  2. Side by side, notation how our HTML references a JavaScript file called index.js, with a line like <script src="index.js" defer></script>. We need to create this and write our JavaScript code into it. Create an alphabetize.js file in the same directory equally your HTML file.
  3. We'll start off past creating references to all the HTML features we demand to manipulate in this case — nosotros'll create them all as constants, as these references do not need to change in the lifecycle of the app. Add the following lines to your JavaScript file:
                                                  // create needed constants                        const                        rememberDiv                        =                        certificate.                        querySelector                        (                        '.remember'                        )                        ;                        const                        forgetDiv                        =                        document.                        querySelector                        (                        '.forget'                        )                        ;                        const                        form                        =                        document.                        querySelector                        (                        'form'                        )                        ;                        const                        nameInput                        =                        document.                        querySelector                        (                        '#entername'                        )                        ;                        const                        submitBtn                        =                        document.                        querySelector                        (                        '#submitname'                        )                        ;                        const                        forgetBtn                        =                        document.                        querySelector                        (                        '#forgetname'                        )                        ;                        const                        h1                        =                        document.                        querySelector                        (                        'h1'                        )                        ;                        const                        personalGreeting                        =                        document.                        querySelector                        (                        '.personal-greeting'                        )                        ;                                          
  4. Next upwards, nosotros need to include a small upshot listener to stop the grade from really submitting itself when the submit push is pressed, as this is non the behavior we want. Add this snippet below your previous code:
                                                  // Stop the form from submitting when a button is pressed                        grade.                        addEventListener                        (                        'submit'                        ,                        eastward                        =>                        e.                        preventDefault                        (                        )                        )                        ;                                          
  5. Now we demand to add together an issue listener, the handler role of which will run when the "Say hullo" button is clicked. The comments explain in item what each bit does, but in essence here nosotros are taking the name the user has entered into the text input box and saving it in spider web storage using setItem(), then running a function called nameDisplayCheck() that will handle updating the actual website text. Add this to the lesser of your code:
                                                  // run function when the 'Say hello' push is clicked                        submitBtn.                        addEventListener                        (                        'click'                        ,                        (                        )                        =>                        {                        // shop the entered name in web storage                        localStorage.                        setItem                        (                        'proper name'                        ,                        nameInput.value)                        ;                        // run nameDisplayCheck() to sort out displaying the personalized greetings and updating the form display                        nameDisplayCheck                        (                        )                        ;                        }                        )                        ;                                          
  6. At this point we also need an upshot handler to run a office when the "Forget" button is clicked — this is only displayed after the "Say how-do-you-do" button has been clicked (the two form states toggle back and forth). In this role nosotros remove the proper noun item from web storage using removeItem(), then again run nameDisplayCheck() to update the brandish. Add this to the bottom:
                                                  // run function when the 'Forget' push button is clicked                        forgetBtn.                        addEventListener                        (                        'click'                        ,                        (                        )                        =>                        {                        // Remove the stored name from web storage                        localStorage.                        removeItem                        (                        'proper noun'                        )                        ;                        // run nameDisplayCheck() to sort out displaying the generic greeting again and updating the course display                        nameDisplayCheck                        (                        )                        ;                        }                        )                        ;                                          
  7. It is now time to define the nameDisplayCheck() function itself. Here we cheque whether the name item has been stored in web storage past using localStorage.getItem('name') as a conditional test. If the proper noun has been stored, this phone call will evaluate to true; if non, the call will evaluate to false. If the call evaluates to true, nosotros display a personalized greeting, display the "forget" part of the form, and hibernate the "Say hello" role of the grade. If the phone call evaluates to false, nosotros brandish a generic greeting and practice the reverse. Over again, put the following code at the bottom:
                                                  // define the nameDisplayCheck() role                        role                        nameDisplayCheck                        (                        )                        {                        // bank check whether the 'proper noun' data item is stored in spider web Storage                        if                        (localStorage.                        getItem                        (                        'name'                        )                        )                        {                        // If information technology is, display personalized greeting                        const                        name                        =                        localStorage.                        getItem                        (                        'name'                        )                        ;                        h1.textContent                        =                                                  `                          Welcome,                                                                                ${name}                                                    `                                                ;                        personalGreeting.textContent                        =                                                  `                          Welcome to our website,                                                                                ${proper noun}                                                    ! We hope you have fun while you are hither.                          `                                                ;                        // hibernate the 'remember' part of the form and testify the 'forget' part                        forgetDiv.manner.display                        =                        'cake'                        ;                        rememberDiv.style.brandish                        =                        'none'                        ;                        }                        else                        {                        // if not, brandish generic greeting                        h1.textContent                        =                        'Welcome to our website '                        ;                        personalGreeting.textContent                        =                        'Welcome to our website. We hope you have fun while you are hither.'                        ;                        // hide the 'forget' part of the form and prove the 'remember' part                        forgetDiv.way.display                        =                        'none'                        ;                        rememberDiv.style.display                        =                        'block'                        ;                        }                        }                                          
  8. Last but not least, we need to run the nameDisplayCheck() role when the folio is loaded. If we don't do this, then the personalized greeting will not persist across page reloads. Add the post-obit to the bottom of your code:

Your case is finished — well done! All that remains at present is to save your code and test your HTML page in a browser. You can run into our finished version running live here.

Note: In the line <script src="index.js" defer></script> of the source for our finished version, the defer aspect specifies that the contents of the <script> element volition not execute until the page has finished loading.

Storing circuitous data — IndexedDB

The IndexedDB API (sometimes abbreviated IDB) is a consummate database arrangement available in the browser in which you can store complex related data, the types of which aren't limited to elementary values similar strings or numbers. You tin can store videos, images, and pretty much annihilation else in an IndexedDB instance.

However, this does come at a toll: IndexedDB is much more complex to utilize than the Web Storage API. In this section, we'll actually only scratch the surface of what it is capable of, but we will requite you enough to get started.

Working through a note storage case

Here we'll run you through an example that allows you to shop notes in your browser and view and delete them whenever you similar, getting you to build it up for yourself and explaining the about fundamental parts of IDB every bit nosotros go on.

The app looks something similar this:

Each note has a championship and some body text, each individually editable. The JavaScript lawmaking we'll go through below has detailed comments to assistance you understand what's going on.

Getting started

  1. First of all, make local copies of our alphabetize.html, mode.css, and index-start.js files into a new directory on your local motorcar.
  2. Have a wait at the files. You'll see that the HTML defines a web site with a header and footer, besides as a main content expanse that contains a place to brandish notes, and a form for entering new notes into the database. The CSS provides some styling to brand it clearer what is going on. The JavaScript file contains five declared constants containing references to the <ul> element the notes will exist displayed in, the title and body <input> elements, the <form> itself, and the <push button>.
  3. Rename your JavaScript file to index.js. You are now fix to start calculation code to it.

Database initial set upwardly

Now let's wait at what we have to practise in the first place, to really gear up a database.

  1. Below the constant declarations, add the post-obit lines:
                                                  // Create an instance of a db object for us to store the open up database in                        allow                        db;                                          
    Hither nosotros are declaring a variable chosen db — this volition later be used to shop an object representing our database. We will employ this in a few places, so nosotros've alleged it globally here to brand things easier.
  2. Next, add the following:
                                                  // Open our database; it is created if information technology doesn't already be                        // (see the upgradeneeded handler below)                        const                        openRequest                        =                        window.indexedDB.                        open                        (                        'notes_db'                        ,                        i                        )                        ;                                          
    This line creates a request to open version 1 of a database called notes_db. If this doesn't already be, it will be created for you by subsequent code. You volition come across this request pattern used very often throughout IndexedDB. Database operations take time. You don't desire to hang the browser while yous expect for the results, so database operations are asynchronous, meaning that instead of happening immediately, they will happen at some betoken in the futurity, and yous become notified when they're done. To handle this in IndexedDB, you lot create a request object (which can be called anything you like — nosotros called it openRequest hither, so it is obvious what it is for). You then utilize event handlers to run code when the request completes, fails, etc., which you'll encounter in utilize below.

    Note: The version number is of import. If you desire to upgrade your database (for example, past irresolute the table structure), you take to run your lawmaking again with an increased version number, dissimilar schema specified inside the upgradeneeded handler (encounter below), etc. Nosotros won't encompass upgrading databases in this tutorial.

  3. Now add together the following issue handlers but beneath your previous addition:
                                                  // fault handler signifies that the database didn't open successfully                        openRequest.                        addEventListener                        (                        'error'                        ,                        (                        )                        =>                        console.                        error                        (                        'Database failed to open'                        )                        )                        ;                        // success handler signifies that the database opened successfully                        openRequest.                        addEventListener                        (                        'success'                        ,                        (                        )                        =>                        {                        console.                        log                        (                        'Database opened successfully'                        )                        ;                        // Shop the opened database object in the db variable. This is used a lot beneath                        db                        =                        openRequest.result;                        // Run the displayData() function to display the notes already in the IDB                        displayData                        (                        )                        ;                        }                        )                        ;                                          
    The fault event handler will run if the system comes dorsum maxim that the asking failed. This allows you lot to respond to this problem. In our example, nosotros only impress a bulletin to the JavaScript console. The success consequence handler volition run if the request returns successfully, meaning the database was successfully opened. If this is the example, an object representing the opened database becomes available in the openRequest.effect property, allowing us to dispense the database. Nosotros store this in the db variable we created before for afterwards use. We likewise run a function called displayData(), which displays the data in the database inside the <ul>. We run it at present so that the notes already in the database are displayed as before long as the page loads. You'll see displayData() defined later on on.
  4. Finally for this section, we'll add together probably the most of import upshot handler for setting up the database: upgradeneeded. This handler runs if the database has not already been set upwardly, or if the database is opened with a bigger version number than the existing stored database (when performing an upgrade). Add together the post-obit code, below your previous handler:
                                                  // Gear up the database tables if this has not already been done                        openRequest.                        addEventListener                        (                        'upgradeneeded'                        ,                        e                        =>                        {                        // Grab a reference to the opened database                        db                        =                        e.target.result;                        // Create an objectStore to store our notes in (basically like a single table)                        // including a machine-incrementing primal                        const                        objectStore                        =                        db.                        createObjectStore                        (                        'notes_os'                        ,                        {                        keyPath                        :                        'id'                        ,                        autoIncrement                        :                        true                        }                        )                        ;                        // Ascertain what data items the objectStore will comprise                        objectStore.                        createIndex                        (                        'title'                        ,                        'title'                        ,                        {                        unique                        :                        simulated                        }                        )                        ;                        objectStore.                        createIndex                        (                        'torso'                        ,                        'body'                        ,                        {                        unique                        :                        false                        }                        )                        ;                        console.                        log                        (                        'Database setup consummate'                        )                        ;                        }                        )                        ;                                          
    This is where we define the schema (construction) of our database; that is, the ready of columns (or fields) it contains. Hither nosotros kickoff grab a reference to the existing database from the result holding of the event's target (eastward.target.result), which is the request object. This is equivalent to the line db = openRequest.result; inside the success upshot handler, but nosotros need to practise this separately hither considering the upgradeneeded event handler (if needed) will run earlier the success event handler, meaning that the db value wouldn't exist bachelor if nosotros didn't do this. Nosotros and so employ IDBDatabase.createObjectStore() to create a new object store within our opened database called notes_os. This is equivalent to a single tabular array in a conventional database system. We've given it the proper name notes, and also specified an autoIncrement cardinal field chosen id — in each new record this will automatically be given an incremented value — the developer doesn't need to fix this explicitly. Beingness the key, the id field volition be used to uniquely place records, such as when deleting or displaying a record. Nosotros besides create two other indexes (fields) using the IDBObjectStore.createIndex() method: title (which will incorporate a championship for each note), and torso (which will contain the body text of the notation).

And then with this database schema prepare upward, when we commencement adding records to the database, each one will be represented every bit an object along these lines:

                                      {                    title                    :                    "Buy milk"                    ,                    body                    :                    "Need both cows milk and soy."                    ,                    id                    :                    8                    }                                  

Adding data to the database

Now let'southward look at how we can add records to the database. This will be done using the form on our page.

Beneath your previous consequence handler, add the following line, which sets upwardly a submit consequence handler that runs a function called addData() when the form is submitted (when the submit <button> is pressed leading to a successful form submission):

                                      // Create a submit event handler and so that when the form is submitted the addData() function is run                    class.                    addEventListener                    (                    'submit'                    ,                    addData)                    ;                                  

Now permit'due south define the addData() function. Add this below your previous line:

                                      // Define the addData() function                    office                    addData                    (                    e                    )                    {                    // forbid default - nosotros don't want the form to submit in the conventional mode                    e.                    preventDefault                    (                    )                    ;                    // grab the values entered into the grade fields and store them in an object fix for existence inserted into the DB                    const                    newItem                    =                    {                    championship                    :                    titleInput.value,                    body                    :                    bodyInput.value                    }                    ;                    // open up a read/write db transaction, ready for adding the information                    const                    transaction                    =                    db.                    transaction                    (                    [                    'notes_os'                    ]                    ,                    'readwrite'                    )                    ;                    // phone call an object store that's already been added to the database                    const                    objectStore                    =                    transaction.                    objectStore                    (                    'notes_os'                    )                    ;                    // Make a request to add our newItem object to the object store                    const                    addRequest                    =                    objectStore.                    add together                    (newItem)                    ;                    addRequest.                    addEventListener                    (                    'success'                    ,                    (                    )                    =>                    {                    // Articulate the form, ready for calculation the next entry                    titleInput.value                    =                    ''                    ;                    bodyInput.value                    =                    ''                    ;                    }                    )                    ;                    // Study on the success of the transaction completing, when everything is done                    transaction.                    addEventListener                    (                    'complete'                    ,                    (                    )                    =>                    {                    console.                    log                    (                    'Transaction completed: database modification finished.'                    )                    ;                    // update the display of data to show the newly added item, by running displayData() again.                    displayData                    (                    )                    ;                    }                    )                    ;                    transaction.                    addEventListener                    (                    'fault'                    ,                    (                    )                    =>                    panel.                    log                    (                    'Transaction not opened due to mistake'                    )                    )                    ;                    }                                  

This is quite complex; breaking it down, we:

  • Run Event.preventDefault() on the outcome object to stop the form actually submitting in the conventional fashion (this would cause a page refresh and spoil the experience).
  • Create an object representing a record to enter into the database, populating it with values from the form inputs. Note that we don't take to explicitly include an id value — as we explained earlier, this is motorcar-populated.
  • Open a readwrite transaction confronting the notes_os object store using the IDBDatabase.transaction() method. This transaction object allows us to admission the object shop so we can exercise something to it, e.k. add a new record.
  • Access the object store using the IDBTransaction.objectStore() method, saving the result in the objectStore variable.
  • Add together the new record to the database using IDBObjectStore.add(). This creates a request object, in the same manner as nosotros've seen earlier.
  • Add a bunch of event handlers to the request and the transaction objects to run lawmaking at critical points in the lifecycle. Once the asking has succeeded, we clear the form inputs prepare for entering the next note. In one case the transaction has completed, we run the displayData() office again to update the display of notes on the page.

Displaying the data

Nosotros've referenced displayData() twice in our code already, and then nosotros'd probably better define information technology. Add this to your code, below the previous office definition:

                                      // Define the displayData() function                    function                    displayData                    (                    )                    {                    // Hither we empty the contents of the listing element each time the display is updated                    // If you didn't exercise this, you'd go duplicates listed each time a new annotation is added                    while                    (listing.firstChild)                    {                    list.                    removeChild                    (list.firstChild)                    ;                    }                    // Open our object shop and then go a cursor - which iterates through all the                    // different data items in the shop                    const                    objectStore                    =                    db.                    transaction                    (                    'notes_os'                    )                    .                    objectStore                    (                    'notes_os'                    )                    ;                    objectStore.                    openCursor                    (                    )                    .                    addEventListener                    (                    'success'                    ,                    e                    =>                    {                    // Get a reference to the cursor                    const                    cursor                    =                    e.target.result;                    // If there is however another data item to iterate through, proceed running this lawmaking                    if                    (cursor)                    {                    // Create a list item, h3, and p to put each data item inside when displaying it                    // structure the HTML fragment, and append it inside the listing                    const                    listItem                    =                    document.                    createElement                    (                    'li'                    )                    ;                    const                    h3                    =                    document.                    createElement                    (                    'h3'                    )                    ;                    const                    para                    =                    document.                    createElement                    (                    'p'                    )                    ;                    listItem.                    appendChild                    (h3)                    ;                    listItem.                    appendChild                    (para)                    ;                    list.                    appendChild                    (listItem)                    ;                    // Put the information from the cursor inside the h3 and para                    h3.textContent                    =                    cursor.value.title;                    para.textContent                    =                    cursor.value.torso;                    // Store the ID of the data detail within an attribute on the listItem, and so we know                    // which item information technology corresponds to. This will be useful later when we want to delete items                    listItem.                    setAttribute                    (                    'data-annotation-id'                    ,                    cursor.value.id)                    ;                    // Create a push and place information technology inside each listItem                    const                    deleteBtn                    =                    document.                    createElement                    (                    'button'                    )                    ;                    listItem.                    appendChild                    (deleteBtn)                    ;                    deleteBtn.textContent                    =                    'Delete'                    ;                    // Fix an result handler so that when the button is clicked, the deleteItem()                    // part is run                    deleteBtn.                    addEventListener                    (                    'click'                    ,                    deleteItem)                    ;                    // Iterate to the side by side item in the cursor                    cursor.                    continue                    (                    )                    ;                    }                    else                    {                    // Again, if listing item is empty, display a 'No notes stored' bulletin                    if                    (                    !list.firstChild)                    {                    const                    listItem                    =                    document.                    createElement                    (                    'li'                    )                    ;                    listItem.textContent                    =                    'No notes stored.'                    list.                    appendChild                    (listItem)                    ;                    }                    // if there are no more cursor items to iterate through, say so                    console.                    log                    (                    'Notes all displayed'                    )                    ;                    }                    }                    )                    ;                    }                                  

Once again, let's suspension this down:

  • First nosotros empty out the <ul> element's content, before then filling it with the updated content. If y'all didn't practice this, yous'd end up with a huge list of duplicated content existence added to with each update.
  • Adjacent, we get a reference to the notes_os object store using IDBDatabase.transaction() and IDBTransaction.objectStore() like nosotros did in addData(), except hither nosotros are chaining them together in one line.
  • The next step is to utilize the IDBObjectStore.openCursor() method to open a request for a cursor — this is a construct that can be used to iterate over the records in an object store. We concatenation a success outcome handler on to the end of this line to make the code more concise — when the cursor is successfully returned, the handler is run.
  • Nosotros go a reference to the cursor itself (an IDBCursor object) using const cursor = e.target.result.
  • Side by side, we bank check to see if the cursor contains a record from the datastore (if(cursor){ ... }) — if so, nosotros create a DOM fragment, populate it with the data from the tape, and insert it into the page (inside the <ul> chemical element). We likewise include a delete button that, when clicked, will delete that note past running the deleteItem() part, which we volition expect at in the next section.
  • At the end of the if block, we employ the IDBCursor.continue() method to accelerate the cursor to the side by side record in the datastore, and run the content of the if block once more. If in that location is another record to iterate to, this causes it to be inserted into the page, and so continue() is run again, and and then on.
  • When in that location are no more records to iterate over, cursor will return undefined, and therefore the else block volition run instead of the if cake. This cake checks whether any notes were inserted into the <ul> — if non, it inserts a bulletin to say no note was stored.

Deleting a annotation

Every bit stated above, when a note's delete button is pressed, the notation is deleted. This is achieved by the deleteItem() function, which looks like then:

                                      // Ascertain the deleteItem() part                    function                    deleteItem                    (                    e                    )                    {                    // retrieve the proper noun of the chore we desire to delete. We need                    // to convert it to a number earlier trying to use it with IDB; IDB primal                    // values are type-sensitive.                    const                    noteId                    =                    Number                    (e.target.parentNode.                    getAttribute                    (                    'data-note-id'                    )                    )                    ;                    // open a database transaction and delete the chore, finding it using the id we retrieved higher up                    const                    transaction                    =                    db.                    transaction                    (                    [                    'notes_os'                    ]                    ,                    'readwrite'                    )                    ;                    const                    objectStore                    =                    transaction.                    objectStore                    (                    'notes_os'                    )                    ;                    const                    deleteRequest                    =                    objectStore.                    delete                    (noteId)                    ;                    // study that the information item has been deleted                    transaction.                    addEventListener                    (                    'complete'                    ,                    (                    )                    =>                    {                    // delete the parent of the push button                    // which is the list item, so information technology is no longer displayed                    e.target.parentNode.parentNode.                    removeChild                    (east.target.parentNode)                    ;                    console.                    log                    (                                          `                      Note                                                                    ${noteId}                                                                    deleted.                      `                                        )                    ;                    // Again, if list item is empty, display a 'No notes stored' bulletin                    if                    (                    !list.firstChild)                    {                    const                    listItem                    =                    certificate.                    createElement                    (                    'li'                    )                    ;                    listItem.textContent                    =                    'No notes stored.'                    ;                    listing.                    appendChild                    (listItem)                    ;                    }                    }                    )                    ;                    }                                  
  • The first part of this could utilise some explaining — nosotros retrieve the ID of the record to be deleted using Number(e.target.parentNode.getAttribute('data-note-id')) — recall that the ID of the record was saved in a data-note-id attribute on the <li> when it was first displayed. We practise yet need to pass the aspect through the global built-in Number() object as it is of datatype string, and therefore wouldn't be recognized by the database, which expects a number.
  • We then become a reference to the object store using the aforementioned design we've seen previously, and apply the IDBObjectStore.delete() method to delete the record from the database, passing it the ID.
  • When the database transaction is complete, we delete the note's <li> from the DOM, and once more do the cheque to come across if the <ul> is now empty, inserting a notation as appropriate.

Then that'south it! Your example should now work.

If you are having trouble with it, experience gratuitous to bank check information technology against our alive example (see the source lawmaking also).

Storing complex information via IndexedDB

As we mentioned above, IndexedDB can be used to shop more than than just text strings. Y'all tin store just virtually annihilation you want, including complex objects such as video or image blobs. And information technology isn't much more than difficult to achieve than any other type of data.

To demonstrate how to do it, we've written another instance called IndexedDB video shop (see it running live here too). When y'all first run the example, information technology downloads all the videos from the network, stores them in an IndexedDB database, and so displays the videos in the UI within <video> elements. The 2d time you run it, it finds the videos in the database and gets them from in that location instead before displaying them — this makes subsequent loads much quicker and less bandwidth-hungry.

Let's walk through the most interesting parts of the example. We won't look at information technology all — a lot of information technology is similar to the previous case, and the code is well-commented.

  1. For this example, we've stored the names of the videos to fetch in an assortment of objects:
                                                  const                        videos                        =                        [                        {                        'name'                        :                        'crystal'                        }                        ,                        {                        'proper name'                        :                        'elf'                        }                        ,                        {                        'name'                        :                        'frog'                        }                        ,                        {                        'proper noun'                        :                        'monster'                        }                        ,                        {                        'name'                        :                        'grunter'                        }                        ,                        {                        'proper noun'                        :                        'rabbit'                        }                        ]                        ;                                          
  2. To starting time with, once the database is successfully opened we run an init() role. This loops through the different video names, trying to load a record identified by each name from the videos database. If each video is found in the database (checked by seeing whether request.result evaluates to true — if the record is not present, it will exist undefined), its video files (stored as blobs) and the video proper name are passed straight to the displayVideo() function to place them in the UI. If not, the video name is passed to the fetchVideoFromNetwork() part to ... you lot guessed it — fetch the video from the network.
                                                  function                        init                        (                        )                        {                        // Loop through the video names one past one                        for                        (                        const                        video                        of                        videos)                        {                        // Open transaction, become object store, and get() each video past name                        const                        objectStore                        =                        db.                        transaction                        (                        'videos_os'                        )                        .                        objectStore                        (                        'videos_os'                        )                        ;                        const                        request                        =                        objectStore.                        get                        (video.proper name)                        ;                        asking.                        addEventListener                        (                        'success'                        ,                        (                        )                        =>                        {                        // If the result exists in the database (is not undefined)                        if                        (request.upshot)                        {                        // Grab the videos from IDB and display them using displayVideo()                        panel.                        log                        (                        'taking videos from IDB'                        )                        ;                        displayVideo                        (request.issue.mp4,                        request.result.webm,                        asking.result.name)                        ;                        }                        else                        {                        // Fetch the videos from the network                        fetchVideoFromNetwork                        (video)                        ;                        }                        }                        )                        ;                        }                        }                                          
  3. The post-obit snippet is taken from inside fetchVideoFromNetwork() — here we fetch MP4 and WebM versions of the video using ii dissever fetch() requests. We then employ the Response.hulk() method to excerpt each response'south torso as a hulk, giving united states of america an object representation of the videos that tin can be stored and displayed later on. Nosotros have a problem here though — these 2 requests are both asynchronous, but we only want to try to brandish or store the video when both promises take fulfilled. Fortunately there is a built-in method that handles such a problem — Promise.all(). This takes ane argument — references to all the private promises you want to cheque for fulfillment placed in an array — and returns a hope which is fulfilled when all the individual promises are fulfilled. Inside the then() handler for this promise, we call the displayVideo() function like nosotros did earlier to display the videos in the UI, then we also call the storeVideo() function to store those videos inside the database.
                                                  // Fetch the MP4 and WebM versions of the video using the fetch() function,                        // then expose their response bodies as blobs                        const                        mp4Blob                        =                        fetch                        (                                                  `                          videos/                                                      ${video.name}                                                    .mp4                          `                                                )                        .                        so                        (                        response                        =>                        response.                        blob                        (                        )                        )                        ;                        const                        webmBlob                        =                        fetch                        (                                                  `                          videos/                                                      ${video.name}                                                    .mp4                          `                                                )                        .                        and so                        (                        response                        =>                        response.                        blob                        (                        )                        )                        ;                        // Only run the side by side code when both promises have fulfilled                        Promise.                        all                        (                        [mp4Blob,                        webmBlob]                        )                        .                        and so                        (                        values                        =>                        {                        // display the video fetched from the network with displayVideo()                        displayVideo                        (values[                        0                        ]                        ,                        values[                        1                        ]                        ,                        video.name)                        ;                        // store it in the IDB using storeVideo()                        storeVideo                        (values[                        0                        ]                        ,                        values[                        1                        ]                        ,                        video.proper noun)                        ;                        }                        )                        ;                                          
  4. Allow'south look at storeVideo() first. This is very similar to the pattern you saw in the previous case for adding information to the database — we open a readwrite transaction and get a reference to our videos_os object store, create an object representing the tape to add to the database, then add it using IDBObjectStore.add().
                                                  // Define the storeVideo() function                        role                        storeVideo                        (                        mp4Blob,                          webmBlob,                          name                        )                        {                        // Open transaction, get object store; make it a readwrite then nosotros can write to the IDB                        const                        objectStore                        =                        db.                        transaction                        (                        [                        'videos_os'                        ]                        ,                        'readwrite'                        )                        .                        objectStore                        (                        'videos_os'                        )                        ;                        // Create a record to add together to the IDB                        const                        tape                        =                        {                        mp4                        :                        mp4Blob,                        webm                        :                        webmBlob,                        name                        :                        proper name                        }                        // Add the record to the IDB using add together()                        const                        request                        =                        objectStore.                        add together                        (record)                        ;                        asking.                        addEventListener                        (                        'success'                        ,                        (                        )                        =>                        console.                        log                        (                        'Record addition attempt finished'                        )                        )                        ;                        asking.                        addEventListener                        (                        'fault'                        ,                        (                        )                        =>                        console.                        error                        (request.error)                        )                        ;                        }                                          
  5. Finally, we have displayVideo(), which creates the DOM elements needed to insert the video in the UI and and then appends them to the page. The near interesting parts of this are those shown below — to actually display our video blobs in a <video> element, we demand to create object URLs (internal URLs that betoken to the video blobs stored in retention) using the URL.createObjectURL() method. One time that is done, nosotros can set the object URLs to be the values of our <source> element'southward src attributes, and it works fine.
                                                  // Define the displayVideo() function                        part                        displayVideo                        (                        mp4Blob,                          webmBlob,                          title                        )                        {                        // Create object URLs out of the blobs                        const                        mp4URL                        =                        URL                        .                        createObjectURL                        (mp4Blob)                        ;                        const                        webmURL                        =                        URL                        .                        createObjectURL                        (webmBlob)                        ;                        // Create DOM elements to embed video in the page                        const                        article                        =                        document.                        createElement                        (                        'article'                        )                        ;                        const                        h2                        =                        document.                        createElement                        (                        'h2'                        )                        ;                        h2.textContent                        =                        title;                        const                        video                        =                        document.                        createElement                        (                        'video'                        )                        ;                        video.controls                        =                        true                        ;                        const                        source1                        =                        document.                        createElement                        (                        'source'                        )                        ;                        source1.src                        =                        mp4URL;                        source1.blazon                        =                        'video/mp4'                        ;                        const                        source2                        =                        document.                        createElement                        (                        'source'                        )                        ;                        source2.src                        =                        webmURL;                        source2.type                        =                        'video/webm'                        ;                        // Embed DOM elements into folio                        section.                        appendChild                        (article)                        ;                        article.                        appendChild                        (h2)                        ;                        article.                        appendChild                        (video)                        ;                        video.                        appendChild                        (source1)                        ;                        video.                        appendChild                        (source2)                        ;                        }                                          

Offline asset storage

The higher up example already shows how to create an app that will shop large assets in an IndexedDB database, fugitive the demand to download them more than once. This is already a great improvement to the user feel, but there is still one thing missing — the principal HTML, CSS, and JavaScript files still demand to be downloaded each time the site is accessed, significant that it won't work when there is no network connection.

This is where Service workers and the closely-related Cache API come up in.

A service worker is a JavaScript file that is registered against a detail origin (web site, or role of a web site at a sure domain) when it is accessed by a browser. When registered, information technology tin control pages bachelor at that origin. Information technology does this by sitting between a loaded page and the network and intercepting network requests aimed at that origin.

When it intercepts a asking, it can do anything you wish to information technology (run into use case ideas), just the classic instance is saving the network responses offline and then providing those in response to a request instead of the responses from the network. In effect, it allows you lot to make a spider web site work completely offline.

The Cache API is another customer-side storage mechanism, with a flake of a difference — it is designed to relieve HTTP responses, and then works very well with service workers.

A service worker instance

Allow's look at an case, to requite you a bit of an idea of what this might look similar. We have created some other version of the video store example we saw in the previous section — this functions identically, except that it also saves the HTML, CSS, and JavaScript in the Cache API via a service worker, allowing the case to run offline!

Meet IndexedDB video shop with service worker running live, and too come across the source lawmaking.

Registering the service worker

The first matter to note is that in that location's an extra scrap of code placed in the main JavaScript file (see alphabetize.js). First nosotros do a feature detection exam to see if the serviceWorker fellow member is available in the Navigator object. If this returns true, then nosotros know that at least the basics of service workers are supported. Inside here we use the ServiceWorkerContainer.register() method to register a service worker independent in the sw.js file against the origin it resides at, and so it can command pages in the aforementioned directory as it, or subdirectories. When its promise fulfills, the service worker is accounted registered.

                                      // Annals service worker to command making site work offline                    if                    (                    'serviceWorker'                    in                    navigator)                    {                    navigator.serviceWorker                    .                    register                    (                    '/learning-area/javascript/apis/client-side-storage/cache-sw/video-store-offline/sw.js'                    )                    .                    so                    (                    (                    )                    =>                    console.                    log                    (                    'Service Worker Registered'                    )                    )                    ;                    }                                  

Note: The given path to the sw.js file is relative to the site origin, not the JavaScript file that contains the code. The service worker is at https://mdn.github.io/learning-expanse/javascript/apis/customer-side-storage/enshroud-sw/video-store-offline/sw.js. The origin is https://mdn.github.io, and therefore the given path has to be /learning-expanse/javascript/apis/client-side-storage/cache-sw/video-store-offline/sw.js. If y'all wanted to host this example on your own server, y'all'd have to change this accordingly. This is rather confusing, but it has to work this fashion for security reasons.

Installing the service worker

The next fourth dimension whatsoever folio nether the service worker's command is accessed (e.g. when the case is reloaded), the service worker is installed against that page, significant that it will starting time controlling information technology. When this occurs, an install event is fired against the service worker; you tin write code inside the service worker itself that volition respond to the installation.

Let's expect at an case, in the sw.js file (the service worker). You'll see that the install listener is registered against cocky. This self keyword is a way to refer to the global scope of the service worker from inside the service worker file.

Inside the install handler we use the ExtendableEvent.waitUntil() method, available on the event object, to betoken that the browser shouldn't complete installation of the service worker until after the promise inside information technology has fulfilled successfully.

Hither is where we see the Enshroud API in action. We use the CacheStorage.open() method to open a new cache object in which responses can be stored (like to an IndexedDB object shop). This promise fulfills with a Cache object representing the video-store enshroud. We then use the Cache.addAll() method to fetch a series of avails and add their responses to the cache.

                  self.                    addEventListener                    (                    'install'                    ,                    e                    =>                    {                    e.                    waitUntil                    (                    caches.                    open                    (                    'video-shop'                    )                    .                    then                    (                    enshroud                    =>                    {                    return                    cache.                    addAll                    (                    [                    '/learning-surface area/javascript/apis/client-side-storage/cache-sw/video-store-offline/'                    ,                    '/learning-surface area/javascript/apis/client-side-storage/cache-sw/video-store-offline/index.html'                    ,                    '/learning-surface area/javascript/apis/client-side-storage/cache-sw/video-shop-offline/index.js'                    ,                    '/learning-area/javascript/apis/client-side-storage/cache-sw/video-store-offline/fashion.css'                    ]                    )                    ;                    }                    )                    )                    ;                    }                    )                    ;                                  

That's information technology for at present, installation washed.

Responding to further requests

With the service worker registered and installed against our HTML folio, and the relevant assets all added to our cache, nosotros are almost ready to go. At that place is simply one more thing to do: write some code to respond to further network requests.

This is what the 2d bit of code in sw.js does. Nosotros add together another listener to the service worker global telescopic, which runs the handler function when the fetch event is raised. This happens whenever the browser makes a request for an asset in the directory the service worker is registered against.

Inside the handler nosotros first log the URL of the requested asset. Nosotros then provide a custom response to the asking, using the FetchEvent.respondWith() method.

Inside this block we utilize CacheStorage.match() to check whether a matching request (i.due east. matches the URL) can exist found in any enshroud. This hope fulfills with the matching response if a lucifer is found, or undefined if it isn't.

If a friction match is found, we return it as the custom response. If not, we fetch() the response from the network and render that instead.

                  self.                    addEventListener                    (                    'fetch'                    ,                    e                    =>                    {                    console.                    log                    (e.request.url)                    ;                    e.                    respondWith                    (                    caches.                    match                    (east.request)                    .                    then                    (                    response                    =>                    response                    ||                    fetch                    (e.asking)                    )                    )                    ;                    }                    )                    ;                                  

And that is information technology for our service worker. At that place is a whole load more you can do with them — for a lot more detail, run into the service worker cookbook. Many thanks to Paul Kinlan for his article Calculation a Service Worker and Offline into your Web App, which inspired this example.

Testing the example offline

To examination our service worker example, you'll need to load information technology a couple of times to make certain information technology is installed. Once this is washed, yous can:

  • Try unplugging your network/turning your Wi-Fi off.
  • Select File > Work Offline if you are using Firefox.
  • Go to the devtools, and then cull Awarding > Service Workers, then check the Offline checkbox if you are using Chrome.

If you refresh your instance page again, you should still encounter it load just fine. Everything is stored offline — the folio assets in a enshroud, and the videos in an IndexedDB database.

Summary

That'due south it for now. We promise you've plant our rundown of client-side storage technologies useful.

Meet also

In this module

purdienotin1969.blogspot.com

Source: https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Client-side_storage

Post a Comment for "How to Load Java Script Again in Footer"