Lesson 06-Browser Object Model (BOM) Operaions

Browser Model Overview

How <script> Elements Work

  • The browser downloads and parses HTML simultaneously, starting parsing before the download completes.
  • Upon encountering a <script> element, parsing pauses, and control is handed to the JavaScript engine.
  • If the <script> references an external file, it is downloaded and executed; otherwise, inline code is executed directly.
  • After the JavaScript engine finishes, control returns to the rendering engine, resuming HTML parsing.

Browser Rendering Engines

Different browsers use different rendering engines:

  • Firefox: Gecko
  • Safari: WebKit
  • Chrome: Blink
  • Internet Explorer: Trident
  • Edge: EdgeHTML

Rendering engines process webpages in four stages:

  1. Parsing: HTML is parsed into a DOM tree, and CSS is parsed into a CSSOM (CSS Object Model).
  2. Object Synthesis: The DOM and CSSOM are combined into a render tree.
  3. Layout: The render tree’s layout is calculated.
  4. Painting: The render tree is painted onto the screen.

Reflow and Repaint

Converting the render tree to a webpage layout is called flow (or layout). Displaying this layout on the page is called paint. Both processes are blocking and resource-intensive.

Script operations, stylesheet changes, and user interactions (e.g., hover effects, scrolling, text input, or resizing windows) can trigger reflow (layout recalculation) and repaint (redrawing). Reflow always triggers repaint, but repaint does not necessarily require reflow. For example:

  • Changing an element’s color triggers only repaint.
  • Changing an element’s layout triggers both reflow and repaint.

Developers should minimize repaint frequency and cost by:

  • Avoiding changes to high-level DOM elements, preferring low-level changes.
  • Avoiding expensive layouts like table or flex.

Optimization Techniques

  • Batch DOM reads and writes together, avoiding interleaving.
  • Cache DOM information.
  • Change styles using CSS classes instead of individual properties.
  • Use documentFragment for DOM operations.
  • Use absolute or fixed positioning for animations to reduce impact on other elements.
  • Show/hide elements only when necessary.
  • Use window.requestAnimationFrame() to defer code execution to the next reflow.
  • Use virtual DOM libraries.

JavaScript Engine

The JavaScript engine reads, processes, and executes JavaScript code in webpages.

JavaScript is an interpreted language, running without compilation. This allows easy execution and modification (e.g., refreshing a page reinterprets code) but incurs higher system overhead and slower performance compared to compiled languages.

Browser JavaScript processing steps:

  1. Lexical Analysis: Code is broken into tokens.
  2. Parsing: Tokens are organized into a syntax tree.
  3. Translation: Code is converted to bytecode.
  4. Interpretation: Bytecode is translated to machine code.

Common JavaScript engines:

  • Chakra (Internet Explorer)
  • Nitro/JavaScriptCore (Safari)
  • Carakan (Opera)
  • SpiderMonkey (Firefox)
  • V8 (Chrome, Chromium)

Window Object

Window Object Properties

  • window.name
  • window.closed
  • window.opener
  • window.self
  • window.window
  • window.frames
  • window.length
  • window.frameElement
  • window.top
  • window.parent
  • window.status
  • window.devicePixelRatio
  • Position and size properties
  • Component properties
  • Global object properties
  • window.isSecureContext

Window Object Methods

  • window.alert()
  • window.prompt()
  • window.confirm()
  • window.open()
  • window.close()
  • window.stop()
  • window.moveTo()
  • window.moveBy()
  • window.resizeTo()
  • window.resizeBy()
  • window.scrollTo()
  • window.scroll()
  • window.scrollBy()
  • window.print()
  • window.focus()
  • window.blur()
  • window.getSelection()
  • window.getComputedStyle()
  • window.matchMedia()
  • window.requestAnimationFrame()
  • window.requestIdleCallback()

Window Object Events

  • load event and onload property
  • error event and onerror property
  • Window event listener properties

Multi-Window Operations

Window References:

  • top: The topmost window
  • parent: The parent window
  • self: The current window

<iframe> Element

window.frames Property

  • Navigator.userAgent
  • Navigator.plugins
  • Navigator.platform
  • Navigator.onLine
  • Navigator.language
  • Navigator.languages
  • Navigator.geolocation
  • Navigator.cookieEnabled
  • Navigator.javaEnabled()
  • Navigator.sendBeacon()

Cookies

document.cookie 
HTTP/1.0 200 OK
Content-type: text/html
Set-Cookie: yummy_cookie=choco
Set-Cookie: tasty_cookie=strawberry
Set-Cookie: <cookie-name>=<cookie-value>; Expires=<date>
Set-Cookie: <cookie-name>=<cookie-value>; Max-Age=<non-zero-digit>
Set-Cookie: <cookie-name>=<cookie-value>; Domain=<domain-value>
Set-Cookie: <cookie-name>=<cookie-value>; Path=<path-value>
Set-Cookie: <cookie-name>=<cookie-value>; Secure
Set-Cookie: <cookie-name>=<cookie-value>; HttpOnly

XMLHttpRequest Object

XMLHttpRequest Instance Properties

  • XMLHttpRequest.readyState
  • XMLHttpRequest.onreadystatechange
  • XMLHttpRequest.response
  • XMLHttpRequest.responseType
  • XMLHttpRequest.responseText
  • XMLHttpRequest.responseXML
  • XMLHttpRequest.responseURL
  • XMLHttpRequest.status, XMLHttpRequest.statusText
  • XMLHttpRequest.timeout, XMLHttpRequestEventTarget.ontimeout
  • Event listener properties
  • XMLHttpRequest.withCredentials
  • XMLHttpRequest.upload

XMLHttpRequest Instance Methods

  • XMLHttpRequest.open()
  • XMLHttpRequest.send()
  • XMLHttpRequest.setRequestHeader()
  • XMLHttpRequest.overrideMimeType()
  • XMLHttpRequest.getResponseHeader()
  • XMLHttpRequest.getAllResponseHeaders()
  • XMLHttpRequest.abort()

XMLHttpRequest Instance Events

  • readyStateChange event
  • progress event
  • load, error, and abort events
  • loadend event
  • timeout event

Same-Origin Policy

Purpose

The same-origin policy ensures user data security by preventing malicious websites from accessing data. Examples:

  • http://www.example.com/dir2/other.html: Same origin
  • http://example.com/dir/other.html: Different origin (different domain)
  • http://v2.www.example.com/dir/other.html: Different origin (different subdomain)
  • http://www.example.com:81/dir/other.html: Different origin (different port)
  • https://www.example.com/dir/page.html: Different origin (different protocol)

Restrictions

  • Cannot read Cookie, LocalStorage, or IndexedDB from different origins.
  • Cannot access the DOM of a different-origin webpage.
  • Cannot send AJAX requests to different-origin addresses (requests can be sent, but responses are blocked by the browser).

JavaScript can access another window’s window object. For different origins, only nine properties and four methods are accessible:

  • window.closed, window.frames, window.length, window.location, window.opener, window.parent, window.self, window.top, window.window
  • window.blur(), window.close(), window.focus(), window.postMessage()

Cookies are server-written data shared only by same-origin pages. If two pages share the same top-level domain but different subdomains, setting document.domain allows cookie sharing.

Cross-Origin Communication

  • window.postMessage()
  • JSONP
  • WebSocket
  • CORS

CORS Communication

CORS (Cross-Origin Resource Sharing) is a W3C standard allowing browsers to send XMLHttpRequest requests to cross-origin servers, bypassing AJAX same-origin restrictions.

CORS requests are categorized as simple requests or non-simple requests.

A request is simple if it meets both conditions:

  1. The method is one of: HEAD, GET, or POST.
  2. Headers are limited to: Accept, Accept-Language, Content-Language, Last-Event-ID, Content-Type (restricted to application/x-www-form-urlencoded, multipart/form-data, or text/plain).

Simple Request

GET /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8

Preflight Request

OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

Storage Interface

Properties and Methods

  • Storage.setItem()
  • Storage.getItem()
  • Storage.removeItem()
  • Storage.clear()
  • Storage.key()
window.localStorage.setItem('foo', 'a');
window.localStorage.setItem('bar', 'b');
window.localStorage.setItem('baz', 'c');
window.sessionStorage.setItem('key', 'value');
window.sessionStorage.getItem('key')
sessionStorage.removeItem('key');
localStorage.removeItem('key');
window.sessionStorage.clear()
window.localStorage.clear()
window.localStorage.length // 3

Storage Event

The listener function receives an event object inheriting the StorageEvent interface with read-only properties:

  • StorageEvent.key: The changed key name (string). Returns null if triggered by clear().
  • StorageEvent.newValue: The new value (string). Returns null if triggered by clear() or key deletion.
  • StorageEvent.oldValue: The old value (string). Returns null for new keys.
  • StorageEvent.storageArea: The storage object containing all key-value pairs for the domain.
  • StorageEvent.url: The URL of the page triggering the storage event.

History Object

Properties

  • History.length: The number of URLs visited in the current window, including the current page.
  • History.state: The topmost state value in the history stack.

Methods

  • History.back(): Moves to the previous URL, equivalent to the browser’s back button. No effect on the first URL.
  • History.forward(): Moves to the next URL, equivalent to the forward button. No effect on the last URL.
  • History.go(): Moves to a URL relative to the current one based on an integer parameter (e.g., go(1) = forward(), go(-1) = back()). No effect if the parameter exceeds the URL range. Default 0 refreshes the page.
  • History.pushState(): Adds a record to the history.
  • History.replaceState(): Modifies the current history record, identical to pushState() otherwise.

Location Object, URL Object, and URLSearchParams Object

Location Object

Properties:

  • Location.href: The full URL.
  • Location.protocol: The protocol, including the colon (:).
  • Location.host: The host, including port if non-standard (80 or 443).
  • Location.hostname: The hostname, excluding port.
  • Location.port: The port number.
  • Location.pathname: The path, starting with /.
  • Location.search: The query string, starting with ?.
  • Location.hash: The fragment identifier, starting with #.
  • Location.username: The username before the domain.
  • Location.password: The password before the domain.
  • Location.origin: The protocol, hostname, and port.

Methods:

  • Location.assign(): Navigates to a new URL.
  • Location.replace(): Navigates to a new URL without adding to history.
  • Location.reload(): Reloads the current URL.
  • Location.toString(): Returns the full URL, equivalent to Location.href.

URL Encoding and Decoding

  • encodeURI()
  • encodeURIComponent()
  • decodeURI()
  • decodeURIComponent()

URL Interface

var url1 = new URL('index.html', 'http://example.com');
url1.href
// "http://example.com/index.html"
var url2 = new URL('page2.html', 'http://example.com/page1.html');
url2.href
// "http://example.com/page2.html"
var url3 = new URL('..', 'http://example.com/a/b.html')
url3.href
// "http://example.com/"

Instance Properties:

  • URL.href: The full URL.
  • URL.protocol: The protocol, ending with :.
  • URL.hostname: The domain name.
  • URL.host: The domain and port, including : (omits default ports 80 and 443).
  • URL.port: The port.
  • URL.origin: The protocol, domain, and port.
  • URL.pathname: The path, starting with /.
  • URL.search: The query string, starting with ?.
  • URL.searchParams: A URLSearchParams instance (unlike Location).
  • URL.hash: The fragment identifier, starting with #.
  • URL.password: The password before the domain.
  • URL.username: The username before the domain.

Static Methods:

  • URL.createObjectURL()
  • URL.revokeObjectURL()

URLSearchParams Object

  • URLSearchParams.toString()
  • URLSearchParams.append()
  • URLSearchParams.delete()
  • URLSearchParams.has()
  • URLSearchParams.set()
  • URLSearchParams.get()
  • URLSearchParams.getAll()
  • URLSearchParams.sort()
  • URLSearchParams.keys()
  • URLSearchParams.values()
  • URLSearchParams.entries()

ArrayBuffer Object and Blob Object

ArrayBuffer Object

Represents a segment of binary data, simulating memory data. JavaScript can read and write binary data using this object.

var buf1 = new ArrayBuffer(8);
buf1.byteLength // 8
var buf2 = buf1.slice(0);

Blob Object

new Blob(array [, options])
var htmlFragment = ['<a id="a"><b id="b">hey!</b></a>'];
var myBlob = new Blob(htmlFragment, {type : 'text/html'});
myBlob.size // 32
myBlob.type // "text/html"

Retrieving File Information:

// HTML: <input type="file" accept="image/*" multiple onchange="fileinfo(this.files)"/>
function fileinfo(files) {
  for (var i = 0; i < files.length; i++) {
    var f = files[i];
    console.log(
      f.name, // Filename without path
      f.size, // File size (Blob property)
      f.type, // File MIME type (Blob property)
      f.lastModifiedDate // Last modified date
    );
  }
}

Downloading Files:

function getBlob(url, callback) {
  var xhr = new XMLHttpRequest();
  xhr.open('GET', url);
  xhr.responseType = 'blob';
  xhr.onload = function () {
    callback(xhr.response);
  }
  xhr.send(null);
}

Generating URLs:

var droptarget = document.getElementById('droptarget');
droptarget.ondrop = function (e) {
  var files = e.dataTransfer.files;
  for (var i = 0; i < files.length; i++) {
    var type = files[i].type;
    if (type.substring(0,6) !== 'image/')
      continue;
    var img = document.createElement('img');
    img.src = URL.createObjectURL(files[i]);
    img.onload = function () {
      this.width = 100;
      document.body.appendChild(this);
      URL.revokeObjectURL(this.src);
    }
  }
}

Reading Files:
FileReader reads Blob or File content:

  • FileReader.readAsText(): Returns text, defaulting to UTF-8.
  • FileReader.readAsArrayBuffer(): Returns an ArrayBuffer.
  • FileReader.readAsDataURL(): Returns a Data URL.
  • FileReader.readAsBinaryString(): Returns a raw binary string.
// HTML: <input type='file' onchange='readfile(this.files[0])'></input>
// <pre id='output'></pre>
function readfile(f) {
  var reader = new FileReader();
  reader.readAsText(f);
  reader.onload = function () {
    var text = reader.result;
    var out = document.getElementById('output');
    out.innerHTML = '';
    out.appendChild(document.createTextNode(text));
  }
  reader.onerror = function(e) {
    console.log('Error', e);
  };
}
// HTML: <input type="file" onchange="typefile(this.files[0])"></input>
function typefile(file) {
  var slice = file.slice(0, 4);
  var reader = new FileReader();
  reader.readAsArrayBuffer(slice);
  reader.onload = function (e) {
    var buffer = reader.result;
    var view = new DataView(buffer);
    var magic = view.getUint32(0, false);
    switch(magic) {
      case 0x89504E47: file.verified_type = 'image/png'; break;
      case 0x47494638: file.verified_type = 'image/gif'; break;
      case 0x25504446: file.verified_type = 'application/pdf'; break;
      case 0x504b0304: file.verified_type = 'application/zip'; break;
    }
    console.log(file.name, file.verified_type);
  };
}

File Object, FileList Object, and FileReader Object

File Object

Represents a file for reading and writing. It inherits from Blob and can be used wherever Blob is applicable.

// HTML: <input id="fileItem" type="file">
var file = document.getElementById('fileItem').files[0];
file instanceof File // true

The native File() constructor creates File instances:

new File(array, name [, options])
  • array: An array of binary objects or strings representing file content.
  • name: A string for the filename or path.
  • options: An optional object for setting instance properties.

Instance Properties and Methods:

  • File.lastModified: Last modification time.
  • File.name: Filename or path.
  • File.size: File size in bytes.
  • File.type: MIME type.

FileList Object

An array-like object representing selected files, with each member being a File instance. Appears in:

  • The files property of <input type="file">.
  • The DataTransfer.files property during drag-and-drop.
// HTML: <input id="fileItem" type="file">
var files = document.getElementById('fileItem').files;
files instanceof FileList // true

FileReader Object

Reads content from File or Blob objects.

Instance Properties:

  • FileReader.error: Error object from reading.
  • FileReader.readyState: Reading state (0: no data loaded, 1: loading, 2: complete).
  • FileReader.result: File content (string or ArrayBuffer).
  • FileReader.onabort: Listener for abort event.
  • FileReader.onerror: Listener for error event.
  • FileReader.onload: Listener for load event, accessing content via result.
  • FileReader.onloadstart: Listener for loadstart event.
  • FileReader.onloadend: Listener for loadend event.
  • FileReader.onprogress: Listener for progress event.

Instance Methods:

  • FileReader.abort(): Terminates reading, setting readyState to 2.
  • FileReader.readAsArrayBuffer(): Reads as ArrayBuffer.
  • FileReader.readAsBinaryString(): Reads as raw binary string.
  • FileReader.readAsDataURL(): Reads as Data URL (Base64-encoded).
  • FileReader.readAsText(): Reads as text string, defaulting to UTF-8.

Forms and FormData Object

FormData Object

var formdata = new FormData(form);
var myForm = document.getElementById('myForm');
var formData = new FormData(myForm);
formData.get('username') // ""
formData.set('username', 'John');
formData.get('username') // "John"

formData.set('username', 'John');
formData.append('username', 'Jane');
formData.get('username') // "John"
formData.getAll('username') // ["John", "Jane"]
formData.append('userpic[]', myFileInput.files[0], 'user1.jpg');
formData.append('userpic[]', myFileInput.files[1], 'user2.jpg');

for (var key of formData.keys()) {
  console.log(key);
}
for (var value of formData.values()) {
  console.log(value);
}
for (var pair of formData.entries()) {
  console.log(pair[0] + ': ' + pair[1]);
}
for (var pair of formData) {
  console.log(pair[0] + ': ' + pair[1]);
}

Instance Methods:

  • FormData.get(key): Returns the first value for a key.
  • FormData.getAll(key): Returns an array of all values for a key.
  • FormData.set(key, value): Sets a key’s value, adding or updating it. Optional third parameter for filenames.
  • FormData.delete(key): Removes a key-value pair.
  • FormData.append(key, value): Adds a key-value pair, allowing duplicates. Optional third parameter for filenames.
  • FormData.has(key): Returns a boolean indicating if the key exists.
  • FormData.keys(): Returns an iterator for keys.
  • FormData.values(): Returns an iterator for values.
  • FormData.entries(): Returns an iterator for key-value pairs.

Form Validation

Automatic Validation:

<input required>
<input pattern="banana|cherry">
<input minlength="6" maxlength="6">
<input type="number" min="1" max="10">
<input type="email">
<input type="URL">
input:invalid {
  border-color: red;
}
input,
input:valid {
  border-color: #ccc;
}

checkValidity():
Manually triggers validation.

form.checkValidity()
formControl.checkValidity()
function submitForm(action) {
  var form = document.getElementById('form');
  form.action = action;
  if (form.checkValidity()) {
    form.submit();
  }
}

willValidate Property:

// HTML: <form novalidate><input id="name" name="name" required /></form>
var input = document.querySelector('#name');
input.willValidate // true

validationMessage Property:
Returns the browser’s error message for invalid controls.

var myInput = document.getElementById('myinput');
if (!myInput.checkValidity()) {
  document.getElementById('prompt').innerHTML = myInput.validationMessage;
}

setCustomValidity():
Customizes validation error messages.

<form action="somefile.php">
  <input
    type="text"
    name="username"
    placeholder="Username"
    pattern="[a-z]{1,15}"
    id="username"
  >
  <input type="submit">
</form>
var input = document.getElementById('username');
input.oninvalid = function (event) {
  event.target.setCustomValidity(
    'Username must be lowercase letters, non-empty, and up to 15 characters'
  );
}

validity Property:

  • ValidityState.badInput: Invalid type conversion.
  • ValidityState.customError: Custom error set via setCustomValidity().
  • ValidityState.patternMismatch: Input does not match pattern.
  • ValidityState.rangeOverflow: Value exceeds maximum.
  • ValidityState.rangeUnderflow: Value below minimum.
  • ValidityState.stepMismatch: Value does not match step.
  • ValidityState.tooLong: Exceeds maximum length.
  • ValidityState.tooShort: Below minimum length.
  • ValidityState.typeMismatch: Invalid type (e.g., email or URL).
  • ValidityState.valid: Meets all validation conditions.
  • ValidityState.valueMissing: Missing required value.

novalidate Attribute:
Disables browser’s automatic validation.

enctype Attribute:
Specifies data encoding for form submission:

  • application/x-www-form-urlencoded: Default for POST.
  • text/plain: Plain text.
  • multipart/form-data: Mixed format for files.

File Upload

<form method="post" enctype="multipart/form-data">
  <div>
    <label for="file">Select a file</label>
    <input type="file" id="file" name="myFile" multiple>
  </div>
  <div>
    <input type="submit" id="submit" name="submit_button" value="Upload" />
  </div>
</form>
var fileSelect = document.getElementById('file');
var files = fileSelect.files;
var formData = new FormData();
for (var i = 0; i < files.length; i++) {
  var file = files[i];
  if (!file.type.match('image.*')) {
    continue;
  }
  formData.append('photos[]', file, file.name);
}
var xhr = new XMLHttpRequest();
xhr.open('POST', 'handler.php', true);
xhr.onload = function () {
  if (xhr.status !== 200) {
    console.log('An error occurred!');
  }
};
xhr.send(formData);

var file = document.getElementById('test-input').files[0];
var xhr = new XMLHttpRequest();
xhr.open('POST', 'myserver/uploads');
xhr.setRequestHeader('Content-Type', file.type);
xhr.send(file);

IndexedDB API

Overview

IndexedDB stores large data volumes, provides lookup interfaces, and supports indexing, unlike LocalStorage. It is a NoSQL database, not relational, and does not support SQL queries.

Features:

  1. Key-Value Storage: Uses object stores, supporting all data types, including JavaScript objects. Data is stored as key-value pairs with unique primary keys.
  2. Asynchronous: Operations do not block the browser, unlike synchronous LocalStorage.
  3. Transactional: Supports transactions, rolling back on any failure.
  4. Same-Origin Restriction: Databases are tied to the creating domain.
  5. Large Storage: Typically at least 250MB, often unlimited.
  6. Binary Support: Stores strings, ArrayBuffer, and Blob.

Concepts:

  • Database: IDBDatabase
  • Object Store: IDBObjectStore
  • Index: IDBIndex
  • Transaction: IDBTransaction
  • Request: IDBRequest
  • Cursor: IDBCursor
  • Key Range: IDBKeyRange

Operations

Opening a Database:

var request = window.indexedDB.open(databaseName, version);
request.onerror = function (event) {
  console.log('Database open error');
};
request.onsuccess = function (event) {
  db = request.result;
  console.log('Database opened successfully');
};
request.onupgradeneeded = function (event) {
  db = event.target.result;
}

Creating a Database:

request.onupgradeneeded = function(event) {
  db = event.target.result;
  var objectStore;
  if (!db.objectStoreNames.contains('person')) {
    objectStore = db.createObjectStore('person', { keyPath: 'id' });
    objectStore.createIndex('name', 'name', { unique: false });
    objectStore.createIndex('email', 'email', { unique: true });
  }
}

Adding Data:

function add() {
  var request = db.transaction(['person'], 'readwrite')
    .objectStore('person')
    .add({ id: 1, name: 'John', age: 24, email: 'john@example.com' });
  request.onsuccess = function (event) {
    console.log('Data added successfully');
  };
  request.onerror = function (event) {
    console.log('Data addition failed');
  }
}
add();

Reading Data:

function read() {
   var transaction = db.transaction(['person']);
   var objectStore = transaction.objectStore('person');
   var request = objectStore.get(1);
   request.onerror = function(event) {
     console.log('Transaction failed');
   };
   request.onsuccess = function( event) {
      if (request.result) {
        console.log('Name: ' + request.result.name);
        console.log('Age: ' + request.result.age);
        console.log('Email: ' + request.result.email);
      } else {
        console.log('No data found');
      }
   };
}
read();

Traversing Data:

function readAll() {
  var objectStore = db.transaction('person').objectStore('person');
   objectStore.openCursor().onsuccess = function (event) {
     var cursor = event.target.result;
     if (cursor) {
       console.log('Id: ' + cursor.key);
       console.log('Name: ' + cursor.value.name);
       console.log('Age: ' + cursor.value.age);
       console.log('Email: ' + cursor.value.email);
       cursor.continue();
    } else {
      console.log('No more data!');
    }
  };
}
readAll();

Updating Data:

function update() {
  var request = db.transaction(['person'], 'readwrite')
    .objectStore('person')
    .put({ id: 1, name: 'Jane', age: 35, email: 'jane@example.com' });
  request.onsuccess = function (event) {
    console.log('Data updated successfully');
  };
  request.onerror = function (event) {
    console.log('Data update failed');
  }
}
update();

Deleting Data:

function remove() {
  var request = db.transaction(['person'], 'readwrite')
    .objectStore('person')
    .delete(1);
  request.onsuccess = function (event) {
    console.log('Data deleted successfully');
  };
}
remove();

Using Indexes:

var transaction = db.transaction(['person'], 'readonly');
var store = transaction.objectStore('person');
var index = store.index('name');
var request = index.get('Jane');
request.onsuccess = function (e) {
  var result = e.target.result;
  if (result) {
    // ...
  } else {
    // ...
  }
}

IndexedDB Objects

indexedDB.open():

var openRequest = indexedDB.open('test', 1);
var db;
openRequest.onupgradeneeded = function (e) {
  console.log('Upgrading...');
}
openRequest.onsuccess = function (e) {
  console.log('Success!');
  db = openRequest.result;
}
openRequest.onerror = function (e) {
  console.log('Error');
  console.log(e);
}

indexedDB.deleteDatabase():

var DBDeleteRequest = window.indexedDB.deleteDatabase('demo');
DBDeleteRequest.onerror = function (event) {
  console.log('Error');
};
DBDeleteRequest.onsuccess = function (event) {
  console.log('success');
};

indexedDB.cmp():
Compares two values as IndexedDB keys, returning 0 (equal), 1 (first > second), or -1 (first < second).

window.indexedDB.cmp(1, 2) // -1

IDBRequest Object

Properties:

  • IDBRequest.readyState: "pending" (operation in progress) or "done" (operation complete).
  • IDBRequest.result: The request result (errors if unavailable).
  • IDBRequest.error: Error object on failure.
  • IDBRequest.source: The request source (e.g., index or object store).
  • IDBRequest.transaction: The current transaction, or null if none.
  • IDBRequest.onsuccess: Success event listener.
  • IDBRequest.onerror: Error event listener.

IDBOpenDBRequest Properties:

  • IDBOpenDBRequest.onblocked: Listener for blocked event (database in use during upgrade).
  • IDBOpenDBRequest.onupgradeneeded: Listener for upgradeneeded event.

IDBDatabase Object

var db;
var DBOpenRequest = window.indexedDB.open('demo', 1);
DBOpenRequest.onerror = function (event) {
  console.log('Error');
};
DBOpenRequest.onsuccess = function(event) {
  db = DBOpenRequest.result;
  // ...
};

Properties:

  • IDBDatabase.name: Database name.
  • IDBDatabase.version: Database version (empty string for new databases).
  • IDBDatabase.objectStoreNames: List of object store names.
  • IDBDatabase.onabort: Listener for abort event.
  • IDBDatabase.onclose: Listener for close event.
  • IDBDatabase.onerror: Listener for error event.
  • IDBDatabase.onversionchange: Listener for version change events.

Methods:

  • IDBDatabase.close(): Closes the connection after transactions complete.
  • IDBDatabase.createObjectStore(): Creates an object store (only in versionchange).
  • IDBDatabase.deleteObjectStore(): Deletes an object store (only in versionchange).
  • IDBDatabase.transaction(): Returns an IDBTransaction.

IDBObjectStore Object

Represents an object store, created by IDBDatabase.createObjectStore().

Properties:

  • IDBObjectStore.indexNames: List of index names.
  • IDBObjectStore.keyPath: Primary key.
  • IDBObjectStore.name: Store name.
  • IDBObjectStore.transaction: Associated transaction.
  • IDBObjectStore.autoIncrement: Boolean for auto-incrementing keys.

Methods:

  • IDBObjectStore.add()
  • IDBObjectStore.put()
  • IDBObjectStore.clear()
  • IDBObjectStore.delete()
  • IDBObjectStore.count()
  • IDBObjectStore.getKey()
  • IDBObjectStore.get()
  • IDBObjectStore.getAll()
  • IDBObjectStore.getAllKeys()
  • IDBObjectStore.index()
  • IDBObjectStore.createIndex()
  • IDBObjectStore.deleteIndex()
  • IDBObjectStore.openCursor()
  • IDBObjectStore.openKeyCursor()

IDBTransaction Object

var db;
var DBOpenRequest = window.indexedDB.open('demo', 1);
DBOpenRequest.onsuccess = function(event) {
  db = DBOpenRequest.result;
  var transaction = db.transaction(['demo'], 'readwrite');
  transaction.oncomplete = function (event) {
    console.log('transaction success');
  };
  transaction.onerror = function (event) {
    console.log('transaction error: ' + transaction.error);
  };
  var objectStore = transaction.objectStore('demo');
  var objectStoreRequest = objectStore.add({ foo: 1 });
  objectStoreRequest.onsuccess = function (event) {
    console.log('add data success');
  };
};

Properties:

  • IDBTransaction.db: The database object.
  • IDBTransaction.error: Transaction error, or null if not failed or completed.
  • IDBTransaction.mode: "readonly" or "readwrite".
  • IDBTransaction.objectStoreNames: List of involved object stores.
  • IDBTransaction.onabort: Listener for abort event.
  • IDBTransaction.oncomplete: Listener for complete event.
  • IDBTransaction.onerror: Listener for error event.

Methods:

  • IDBTransaction.abort(): Terminates and rolls back the transaction.
  • IDBTransaction.objectStore(name): Returns the specified object store.

IDBIndex Object

Represents a database index for retrieving records by non-primary keys.

var transaction = db.transaction(['contactsList'], 'readonly');
var objectStore = transaction.objectStore('contactsList');
var myIndex = objectStore.index('lName');
myIndex.openCursor().onsuccess = function (event) {
  var cursor = event.target.result;
  if (cursor) {
    var tableRow = document.createElement('tr');
    tableRow.innerHTML =   '<td>' + cursor.value.id + '</td>'
                         + '<td>' + cursor.value.lName + '</td>'
                         + '<td>' + cursor.value.fName + '</td>'
                         + '<td>' + cursor.value.jTitle + '</td>'
                         + '<td>' + cursor.value.company + '</td>'
                         + '<td>' + cursor.value.eMail + '</td>'
                         + '<td>' + cursor.value.phone + '</td>'
                         + '<td>' + cursor.value.age + '</td>';
    tableEntry.appendChild(tableRow);
    cursor.continue();
  } else {
    console.log('Entries all displayed.');
  }
};

Properties:

  • IDBIndex.name: Index name.
  • IDBIndex.objectStore: Associated object store.
  • IDBIndex.keyPath: Index key.
  • IDBIndex.multiEntry: Boolean for array key paths (each array element indexed if true).
  • IDBIndex.unique: Boolean for unique keys.

Methods (all asynchronous, returning IDBRequest):

  • IDBIndex.count(): Counts records, optionally filtered by key or range.
  • IDBIndex.get(key): Retrieves a record by key.
  • IDBIndex.getKey(key): Retrieves a key.
  • IDBIndex.getAll(): Retrieves all records, optionally filtered.
  • IDBIndex.getAllKeys(): Retrieves all keys.
  • IDBIndex.openCursor(): Traverses index entries with a cursor.
  • IDBIndex.openKeyCursor(): Traverses index keys with a cursor.

IDBCursor Object

Represents a cursor for traversing object stores or indexes.

var transaction = db.transaction(['rushAlbumList'], 'readonly');
var objectStore = transaction.objectStore('rushAlbumList');
objectStore.openCursor(null, 'next').onsuccess = function(event) {
  var cursor = event.target.result;
  if (cursor) {
    var listItem = document.createElement('li');
    listItem.innerHTML = cursor.value.albumTitle + ', ' + cursor.value.year;
    list.appendChild(listItem);
    console.log(cursor.source);
    cursor.continue();
  } else {
    console.log('Entries all displayed.');
  }
};

Properties:

  • IDBCursor.source: The object store or index being traversed.
  • IDBCursor.direction: Traversal direction (next, nextunique, prev, prevunique).
  • IDBCursor.key: Current record’s key.
  • IDBCursor.value: Current record’s value.
  • IDBCursor.primaryKey: Current record’s primary key (equals key for stores, differs for indexes).

Methods:

  • IDBCursor.advance(n): Moves forward n positions.
  • IDBCursor.continue(): Moves forward one position, optionally to a specified key.
  • IDBCursor.continuePrimaryKey(): Moves to a position matching a key and primary key.
  • IDBCursor.delete(): Deletes the current record.
  • IDBCursor.update(): Updates the current record.

IDBKeyRange Object

Represents a range of primary keys for retrieving records.

Static Methods:

  • IDBKeyRange.lowerBound(): Sets the lower bound.
  • IDBKeyRange.upperBound(): Sets the upper bound.
  • IDBKeyRange.bound(): Sets both bounds.
  • IDBKeyRange.only(): Specifies a single value.

Properties:

  • IDBKeyRange.lower: Lower bound.
  • IDBKeyRange.lowerOpen: Boolean indicating if the lower bound is exclusive.
  • IDBKeyRange.upper: Upper bound.
  • IDBKeyRange.upperOpen: Boolean indicating if the upper bound is exclusive.

Web Worker

Key Considerations:

  1. Same-Origin Restriction: Worker scripts must be same-origin as the main thread.
  2. DOM Restrictions: Workers cannot access the main thread’s DOM, document, window, or parent, but can use navigator and location.
  3. Global Object Restrictions: Workers use WorkerGlobalScope, not Window. Some interfaces (e.g., console) are unsupported in theory but often available.
  4. Communication: Workers and the main thread communicate via messages, not direct interaction.
  5. Script Restrictions: Workers cannot use alert() or confirm() but can send AJAX requests.
  6. File Restrictions: Workers cannot access local files (file://); scripts must be network-loaded.
var worker = new Worker('work.js');
worker.postMessage('Hello World');
worker.postMessage({method: 'echo', args: ['Work']});
worker.onmessage = function (event) {
  doSomething(event.data);
}
function doSomething() {
  worker.postMessage('Work done!');
}
self.addEventListener('message', function (e) {
  self.postMessage('You said: ' + e.data);
}, false);

self.addEventListener('message', function (e) {
  var data = e.data;
  switch (data.cmd) {
    case 'start':
      self.postMessage('WORKER STARTED: ' + data.msg);
      break;
    case 'stop':
      self.postMessage('WORKER STOPPED: ' + data.msg);
      self.close();
      break;
    default:
      self.postMessage('Unknown command: ' + data.msg);
  };
}, false);

worker.onerror(function (event) {
  console.log([
    'ERROR: Line ', event.lineno, ' in ', event.filename, ': ', event.message
  ].join(''));
});

worker.terminate();
self.close();

Polling with Workers:

function createWorker(f) {
  var blob = new Blob(['(' + f.toString() + ')()']);
  var url = window.URL.createObjectURL(blob);
  var worker = new Worker(url);
  return worker;
}
var pollingWorker = createWorker(function (e) {
  var cache;
  function compare(new, old) { ... };
  setInterval(function () {
    fetch('/my-api-endpoint').then(function (res) {
      var data = res.json();
      if (!compare(data, cache)) {
        cache = data;
        self.postMessage(data);
      }
    })
  }, 1000)
});
pollingWorker.onmessage = function () {
  // render data
}
pollingWorker.postMessage('init');

Nested Workers:

var worker = new Worker('worker.js');
worker.onmessage = function (event) {
  document.getElementById('result').textContent = event.data;
};

// worker.js
var num_workers = 10;
var items_per_worker = 1000000;
var result = 0;
var pending_workers = num_workers;
for (var i = 0; i < num_workers; i += 1) {
  var worker = new Worker('core.js');
  worker.postMessage(i * items_per_worker);
  worker.postMessage((i + 1) * items_per_worker);
  worker.onmessage = storeResult;
}
function storeResult(event) {
  result += event.data;
  pending_workers -= 1;
  if (pending_workers <= 0)
    postMessage(result);
}

// core.js
var start;
onmessage = getStart;
function getStart(event) {
  start = event.data;
  onmessage = getEnd;
}
var end;
function getEnd(event) {
  end = event.data;
  onmessage = null;
  work();
}
function work() {
  var result = 0;
  for (var i = start; i < end; i += 1) {
    result += 1;
  }
  postMessage(result);
  close();
}
Share your love