JavaScript og IndexedDB
I denne artikkelen skal vi forklare JavaScript og IndexedDB.
Denne veiledningen gir en trinnvis forklaring på JavaScript og IndexedDB
, inkludert praktiske eksempelkoder på hvert steg for å hjelpe deg å utdype forståelsen din.
YouTube Video
javascript-indexed-db.html
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <title>JavaScript & HTML</title>
6 <style>
7 * {
8 box-sizing: border-box;
9 }
10
11 body {
12 margin: 0;
13 padding: 1em;
14 padding-bottom: 10em;
15 font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
16 background-color: #f7f9fc;
17 color: #333;
18 line-height: 1.6;
19 }
20
21 .container {
22 max-width: 800px;
23 margin: 0 auto;
24 padding: 1em;
25 background-color: #ffffff;
26 border: 1px solid #ccc;
27 border-radius: 10px;
28 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
29 }
30
31 .container-flex {
32 display: flex;
33 flex-wrap: wrap;
34 gap: 2em;
35 max-width: 1000px;
36 margin: 0 auto;
37 padding: 1em;
38 background-color: #ffffff;
39 border: 1px solid #ccc;
40 border-radius: 10px;
41 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
42 }
43
44 .left-column, .right-column {
45 flex: 1 1 200px;
46 min-width: 200px;
47 }
48
49 h1, h2 {
50 font-size: 1.2rem;
51 color: #007bff;
52 margin-top: 0.5em;
53 margin-bottom: 0.5em;
54 border-left: 5px solid #007bff;
55 padding-left: 0.6em;
56 background-color: #e9f2ff;
57 }
58
59 button {
60 display: block;
61 margin: 1em auto;
62 padding: 0.75em 1.5em;
63 font-size: 1rem;
64 background-color: #007bff;
65 color: white;
66 border: none;
67 border-radius: 6px;
68 cursor: pointer;
69 transition: background-color 0.3s ease;
70 }
71
72 button:hover {
73 background-color: #0056b3;
74 }
75
76 #output {
77 margin-top: 1em;
78 background-color: #1e1e1e;
79 color: #0f0;
80 padding: 1em;
81 border-radius: 8px;
82 min-height: 200px;
83 font-family: Consolas, monospace;
84 font-size: 0.95rem;
85 overflow-y: auto;
86 white-space: pre-wrap;
87 }
88
89 .highlight {
90 outline: 3px solid #ffc107; /* yellow border */
91 background-color: #fff8e1; /* soft yellow background */
92 transition: background-color 0.3s ease, outline 0.3s ease;
93 }
94
95 .active {
96 background-color: #28a745; /* green background */
97 color: #fff;
98 box-shadow: 0 0 10px rgba(40, 167, 69, 0.5);
99 transition: background-color 0.3s ease, box-shadow 0.3s ease;
100 }
101 </style>
102</head>
103<body>
104 <div class="container">
105 <h1>JavaScript Console</h1>
106 <button id="executeBtn">Execute</button>
107 <div id="output"></div>
108 </div>
109
110 <script>
111 // Override console.log to display messages in the #output element
112 (function () {
113 // Override console.log
114 const originalLog = console.log;
115 console.log = function (...args) {
116 originalLog.apply(console, args);
117 const message = document.createElement('div');
118 message.textContent = args
119 .map(arg => (typeof arg === "object" && arg !== null ? JSON.stringify(arg) : String(arg)))
120 .join(" ");
121 output.appendChild(message);
122 };
123
124 // Override console.error
125 const originalError = console.error;
126 console.error = function (...args) {
127 originalError.apply(console, args);
128 const message = document.createElement('div');
129 message.textContent = args
130 .map(arg => (typeof arg === "object" && arg !== null ? JSON.stringify(arg) : String(arg)))
131 .join(" ");
132 message.style.color = 'red'; // Color error messages red
133 output.appendChild(message);
134 };
135 })();
136
137 document.getElementById('executeBtn').addEventListener('click', () => {
138 // Prevent multiple loads
139 if (document.getElementById('externalScript')) return;
140
141 const script = document.createElement('script');
142 script.src = 'javascript-indexed-db.js';
143 script.id = 'externalScript';
144 //script.onload = () => console.log('javascript-indexed-db.js loaded and executed.');
145 //script.onerror = () => console.log('Failed to load javascript-indexed-db.js.');
146 document.body.appendChild(script);
147 });
148 </script>
149</body>
150</html>
JavaScript og IndexedDB
IndexedDB
er en asynkron nøkkel-verdidatabase som er innebygd i nettlesere. Den tilbyr funksjoner som ligner relasjonsdatabaser og lar deg lagre og søke i store mengder strukturert data på klientsiden. Den er spesielt nyttig for applikasjoner med offline-funksjonalitet og PWA-er (Progressive Web Apps).
Funksjoner til IndexedDB
- Fungerer på en asynkron og hendelsesdrevet måte.
- JavaScript-objekter kan lagres i objektlagre.
- Søk via spørringer eller indekser er mulig.
- Den har stor lagringskapasitet (hundrevis av MB eller mer), slik at du kan lagre mye mer data enn med cookies eller localStorage.
Åpne og opprette en database
For å bruke IndexedDB
må du først åpne en database. Hvis den ikke eksisterer, blir den opprettet automatisk.
1const request = indexedDB.open('MyDatabase', 1); // Specify DB name and version
open
-metoden åpner databasen asynkront og utløser de følgende tre hendelsene.
onsuccess
1// Fired when database is successfully opened
2request.onsuccess = (event) => {
3 const db = event.target.result; // Database instance
4 console.log('Database opened successfully:', db.name);
5};
onsuccess
-hendelsen utløses når databasen åpnes vellykket. Påfølgende operasjoner bør utføres ved å brukerequest.result
, som blir tilgjengelig på dette punktet.
onerror
1// Fired when database fails to open
2request.onerror = (event) => {
3 console.error('Failed to open database:', event.target.error);
4};
onerror
-hendelsen utløses når databasen ikke kan åpnes. Feillogging og feilbehandling bør utføres her.
onupgradeneeded
1// Fired when database is newly created or upgraded
2request.onupgradeneeded = (event) => {
3 const db = event.target.result;
4 console.log('Database upgrade needed (or newly created):', db.name);
5
6 // Example: Create an object store (like a table) if it doesn’t exist
7 if (!db.objectStoreNames.contains('users')) {
8 db.createObjectStore('users', { keyPath: 'id' });
9 console.log('Object store "users" created');
10 }
11};
onupgradeneeded
utløses når databasen opprettes på nytt eller når den spesifiserte versjonen er høyere enn den nåværende. Opprettelse av tabeller (objektlagre) og definering av skjemaet bør gjøres på dette tidspunktet.
Opprette et objektlager
Først oppretter du et 'objektlager' (tilsvarer en tabell) inne i onupgradeneeded
.
1request.onupgradeneeded = function (event) {
2 const db = event.target.result;
3 console.log("onupgradeneeded triggered. Database version:", db.version);
4 if (!db.objectStoreNames.contains('users')) {
5 console.log("Creating object store: users");
6 const store = db.createObjectStore('users', { keyPath: 'id' });
7 store.createIndex('name', 'name', { unique: false });
8 }
9};
Her brukes følgende innstillinger:.
-
createObjectStore
createObjectStore
er en metode for å opprette et nytt objektlager i databasen. Du kan lagre poster i et objektlager og definere hvordan data håndteres ved å angikeyPath
og andre alternativer. Denne prosessen må gjøres inne ionupgradeneeded
-hendelsen. -
keyPath: 'id'
AngikeyPath
tilid
-egenskapen, som entydig identifiserer hver post som en primærnøkkel. Dette gjør atid
kan brukes automatisk når du legger til, søker eller oppdaterer data. -
createIndex
BrukcreateIndex
-metoden for å opprette en indeks basert påname
-egenskapen for søk. Ved å setteunique: false
tillates flere poster med sammename
.
Håndtere vellykkede databasetilkoblinger
Tildel prosesser som skal utføres ved en vellykket databasetilkobling til onsuccess
-hendelsen. I denne prosessen får du database-instansen og forbereder lesing og skriving av data.
1request.onsuccess = function (event) {
2 const db = event.target.result;
3 console.log('Database opened successfully');
4 // Use db for reading and writing in subsequent operations
5};
-
Denne prosessen kjøres når
IndexedDB
-databasetilkoblingen er fullført. -
event.target.result
inneholder den åpnede databaseinstansen (IDBDatabase
-objektet), som brukes for å starte transaksjoner og få tilgang til objektlagre. -
Faktiske lese- og skriveoperasjoner som å legge til, hente, oppdatere og slette data gjøres med
db
-objektet.
På dette punktet er databasen klar, så du kan trygt starte transaksjoner.
Legge til data
Slik legger du til nye data i IndexedDB
.
1function addUser(db, user) {
2 const transaction = db.transaction('users', 'readwrite');
3 const store = transaction.objectStore('users');
4 const request = store.add(user);
5
6 request.onsuccess = () => {
7 console.log('User added:', user);
8 };
9
10 request.onerror = () => {
11 console.error('Add failed:', request.error);
12 };
13}
14
15// Example: Add a user
16request.onsuccess = function (event) {
17 const db = event.target.result;
18 addUser(db, { id: 1, name: 'Alice' });
19 addUser(db, { id: 2, name: 'Bob' });
20 addUser(db, { id: 3, name: 'John' });
21};
- Lag en transaksjon med
db.transaction()
og angi hvilket objektlager du skal operere på, samt modus (i dette tilfellet,readwrite
). - Legg til nye data ved å bruke
store.add()
-metoden. - Transaksjoner forpliktes automatisk, men hvis du vil gruppere flere operasjoner, kan du håndtere det med transaksjonens slutt-hendelser.
Hente data (primærnøkkelsøk)
Slik henter du spesifikke data ved hjelp av primærnøkkelen.
1function getUserById(db, id) {
2 const transaction = db.transaction('users', 'readonly');
3 const store = transaction.objectStore('users');
4 const request = store.get(id);
5
6 request.onsuccess = () => {
7 if (request.result) {
8 console.log('User found:', request.result);
9 } else {
10 console.log('User not found');
11 }
12 };
13
14 request.onerror = () => {
15 console.error('Error retrieving user:', request.error);
16 };
17}
18
19// Example: Get a user by id
20request.onsuccess = function (event) {
21 const db = event.target.result;
22 getUserById(db, 1);
23};
- Opprett en skrivebeskyttet transaksjon med
db.transaction()
. - Bruk
store.get(id)
for å hente data som samsvarer med den angitte primærnøkkelen. onsuccess
kalles når henting lykkes, og resultatet vises hvis det finnes. Hvis det ikke finnes noe resultat, behandles det som 'ingen tilsvarende data.'.
Bruke indekssøk
Hvis du vil søke etter egenskaper andre enn primærnøkkelen, bruk indeksen du opprettet tidligere.
1function getUserByName(db, name) {
2 const transaction = db.transaction('users', 'readonly');
3 const store = transaction.objectStore('users');
4 const index = store.index('name');
5 const request = index.get(name);
6
7 request.onsuccess = () => {
8 if (request.result) {
9 console.log('User by name:', request.result);
10 } else {
11 console.log('No user found with that name');
12 }
13 };
14
15 request.onerror = () => {
16 console.error('Error retrieving user by name:', request.error);
17 };
18}
19
20// Example: Get a user by id
21request.onsuccess = function (event) {
22 const db = event.target.result;
23 getUserByName(db, 'Alice');
24};
- Få tilgang til den forhåndsopprettede
name
-indeksen ved å brukestore.index('name')
. index.get(value)
henter den første posten med tilsvarende verdi. Hvis flere poster har samme verdi, kan du hente alle medindex.getAll(value)
.
Oppdatere data
Bruk put()
-metoden for å oppdatere eksisterende data (overskrive).
Hvis en post med tilsvarende primærnøkkel finnes, blir den oppdatert; hvis ikke, legges en ny post til.
1function updateUser(db, updatedUser) {
2 const transaction = db.transaction('users', 'readwrite');
3 const store = transaction.objectStore('users');
4 const request = store.put(updatedUser);
5
6 request.onsuccess = () => {
7 console.log('User updated:', updatedUser);
8 };
9
10 request.onerror = () => {
11 console.error('Update failed:', request.error);
12 };
13}
14
15// Example: Update user
16request.onsuccess = async (event) => {
17 const db = event.target.result;
18
19 // Test : update existing user
20 updateUser(db, { id: 3, name: 'John Updated' });
21
22 // Test : insert new user
23 updateUser(db, { id: 4, name: 'Charlie' });
24};
put()
er en praktisk metode som støtter både oppdatering og innsetting.- Hvis data med samme primærnøkkel allerede finnes, blir det overskrevet.
- Hvis du vil kontrollere om data finnes før du oppdaterer, kan du bruke
get()
for å sjekke på forhånd.
Slette data
For å slette data som samsvarer med en angitt primærnøkkel, bruk delete()
-metoden.
1function deleteUser(db, id) {
2 const transaction = db.transaction('users', 'readwrite');
3 const store = transaction.objectStore('users');
4 const request = store.delete(id);
5
6 request.onsuccess = () => {
7 console.log(`User with id=${id} deleted successfully`);
8 };
9
10 request.onerror = () => {
11 console.error(`Failed to delete user with id=${id}:`, request.error);
12 };
13}
14
15// Example: Delete a user by id
16request.onsuccess = function (event) {
17 const db = event.target.result;
18 deleteUser(db, 4);
19};
- Bruk
store.delete(id)
for å slette data med tilsvarende primærnøkkel. - Merk at selv om dataene ikke finnes, vil det ikke oppstå noen feil, og operasjonen regnes som vellykket.
- Implementering av feilbehandling vil gi mer robust kode.
Hente alle data
getAll()
For å hente alle poster i objektlageret, bruk getAll()
-metoden.
1function getAllUsers(db) {
2 const transaction = db.transaction('users', 'readonly');
3 const store = transaction.objectStore('users');
4 const request = store.getAll();
5
6 request.onsuccess = () => {
7 console.log('All users:', request.result);
8 };
9
10 request.onerror = () => {
11 console.error('Failed to retrieve users:', request.error);
12 };
13}
14
15// Example: Get all users
16request.onsuccess = function (event) {
17 const db = event.target.result;
18 getAllUsers(db);
19};
getAll()
henter alle poster i det angitte objektlageret som en matrise (array).- Selv når store datamengder hentes på én gang, håndteres det effektivt.
- Resultatene lagres som en matrise i
request.result
. - Legg alltid til feilbehandling slik at du kan håndtere feil.
openCursor()
openCursor()
er en metode for sekvensielt å gå gjennom poster i et objektlager eller en indeks. Det er nyttig når du vil behandle data én post om gangen i stedet for å hente ut store mengder på en gang.
1function getAllUsersWithCursor(db) {
2 const transaction = db.transaction('users', 'readonly');
3 const store = transaction.objectStore('users');
4 const request = store.openCursor();
5
6 request.onsuccess = () => {
7 const cursor = request.result;
8 if (cursor) {
9 console.log('User:', cursor.value); // Process the current record
10 cursor.continue(); // Move to the next record
11 } else {
12 console.log('All users have been processed.');
13 }
14 };
15
16 request.onerror = () => {
17 console.error('Failed to open cursor:', request.error);
18 };
19}
20
21// Example: Get all users
22request.onsuccess = function (event) {
23 const db = event.target.result;
24 getAllUsersWithCursor(db);
25};
- Ved å bruke
openCursor()
starter du en markør/cursor og henter alle poster i objektlageret én etter én. - Bruk
cursor.value
for å hente dataobjektet til den gjeldende posten. - Flytt markøren til neste post med
cursor.continue()
. - Når
cursor === null
, er alle poster blitt gjennomgått.
Eksempel på en oppdateringsprosess ved bruk av openCursor()
For eksempel, prosessen med å endre navnet til en bruker med name
lik Alice
til Alicia
vil se slik ut:.
1function updateUserName(db, oldName, newName) {
2 const transaction = db.transaction('users', 'readwrite');
3 const store = transaction.objectStore('users');
4 const index = store.index('name'); // Use the 'name' index
5 const request = index.openCursor(IDBKeyRange.only(oldName));
6
7 request.onsuccess = () => {
8 const cursor = request.result;
9 if (cursor) {
10 const user = cursor.value;
11 user.name = newName; // Update the name
12 const updateRequest = cursor.update(user);
13
14 updateRequest.onsuccess = () => {
15 console.log('Updated user:', user);
16 };
17
18 updateRequest.onerror = () => {
19 console.error('Failed to update user:', updateRequest.error);
20 };
21
22 cursor.continue();
23 } else {
24 console.log('All matching users have been updated.');
25 }
26 };
27
28 request.onerror = () => {
29 console.error('Cursor error:', request.error);
30 };
31}
32
33// Example: Update user name
34request.onsuccess = function (event) {
35 const db = event.target.result;
36 updateUserName(db, 'Alice', 'Alicia');
37};
-
IDBKeyRange.only(oldName)
Ved å brukeIDBKeyRange.only
, kan du kun målrette de postene hvor nøkkelen nøyaktig samsvarer medoldName
. Dette er nyttig når du ønsker å få direkte tilgang til en bestemt verdi. -
cursor.update()
Etter å ha oppdatertcursor.value
, vil et kall tilupdate()
overskrive den tilsvarende posten. -
Håndtering av flere treff Ved å kalle
cursor.continue()
, kan du flytte markøren til neste samsvarende post. Dette gjør det mulig å behandle flere poster som samsvarer med samme nøkkel eller betingelse i rekkefølge. -
Feilhåndtering Ved å loggføre i
onerror
når en prosess feiler, blir det enklere å undersøke årsaker og feilsøke under drift.
Eksempel på en sletteprosess ved bruk av openCursor()
For eksempel, prosessen med å slette alle brukere med name
lik Bob
vil se slik ut:.
1function deleteUsersByName(db, targetName) {
2 const transaction = db.transaction('users', 'readwrite');
3 const store = transaction.objectStore('users');
4 const index = store.index('name');
5 const request = index.openCursor(IDBKeyRange.only(targetName));
6
7 request.onsuccess = () => {
8 const cursor = request.result;
9 if (cursor) {
10 const deleteRequest = cursor.delete();
11
12 deleteRequest.onsuccess = () => {
13 console.log('Deleted user:', cursor.value);
14 };
15
16 deleteRequest.onerror = () => {
17 console.error('Failed to delete user:', deleteRequest.error);
18 };
19
20 cursor.continue();
21 } else {
22 console.log('All matching users have been deleted.');
23 }
24 };
25
26 request.onerror = () => {
27 console.error('Cursor error:', request.error);
28 };
29}
30
31// Example: Delete user by name
32request.onsuccess = function (event) {
33 const db = event.target.result;
34 deleteUsersByName(db, 'Bob');
35};
cursor.delete()
Medcursor.delete()
slettes posten på den nåværende markørposisjonen. Siden resultatet returneres asynkront, kan du sjekke prosessen ionsuccess
.
Notater om transaksjoner og asynkron behandling
IndexedDB
er asynkron og hendelsesdrevet. Alle operasjoner må håndteres med onsuccess
- eller onerror
-hendelser. Når du ønsker å gruppere flere prosesser sammen, er det praktisk å kapsle dem i et Promise
.
1function openDatabase() {
2 return new Promise((resolve, reject) => {
3 const request = indexedDB.open('MyDatabase', 1);
4
5 request.onupgradeneeded = function (event) {
6 const db = event.target.result;
7
8 // Initialization process (creating object stores and indexes, etc.)
9 const store = db.createObjectStore('users', { keyPath: 'id' });
10 store.createIndex('name', 'name', { unique: false });
11 };
12
13 request.onsuccess = function (event) {
14 const db = event.target.result;
15 resolve(db);
16 };
17
18 request.onerror = function () {
19 reject(request.error);
20 };
21 });
22}
23
24function addUserAsync(db, user) {
25 return new Promise((resolve, reject) => {
26 const transaction = db.transaction('users', 'readwrite');
27 const store = transaction.objectStore('users');
28 const request = store.add(user);
29
30 request.onsuccess = () => resolve();
31 request.onerror = () => reject(request.error);
32 });
33}
34
35async function main() {
36 try {
37 const db = await openDatabase();
38 await addUserAsync(db, { id: 1, name: 'Alice' });
39 console.log('User added successfully');
40 } catch (error) {
41 console.error('Error:', error);
42 }
43}
- Ved å pakke inn funksjoner som
openDatabase
elleraddUserAsync
medPromise
, blir det mulig å håndtere asynkrone prosesser intuitivt medasync/await
. - Dette unngår callback-helvete og gjør koden mer lesbar.
Sammendrag
IndexedDB
er en svært kraftig funksjon når du ønsker å utføre avansert databehandling på klientsiden. I begynnelsen kan du bli forvirret av hendelsesbasert asynkron behandling, men når du først forstår strukturen, kan du utføre komplette dataoperasjoner på klientsiden.
Spesielt vil det å huske på følgende punkter hjelpe deg å bruke det mer smidig:.
- Utfør innledende oppsett med
onupgradeneeded
. - Vær oppmerksom på lese/skrive-modus for transaksjoner.
- Indekser gjør effektivt søk mulig.
- Data kan lagres som objekter, noe som gjør det svært kompatibelt med JSON.
Ved å mestre IndexedDB
blir databehandling for PWA-er og offline-apper mye enklere.
Du kan følge med på artikkelen ovenfor ved å bruke Visual Studio Code på vår YouTube-kanal. Vennligst sjekk ut YouTube-kanalen.