aboutsummaryrefslogtreecommitdiff
path: root/files/fr/web/api/indexeddb_api/using_indexeddb/index.html
blob: 39097580d49a85d2087bb034f7a9f9c0b19da4bb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
---
title: Utiliser IndexedDB
slug: Web/API/IndexedDB_API/Using_IndexedDB
tags:
  - Avancé
  - Base de données
  - Guide
  - IndexedDB
  - Stockage
  - Tutoriel
translation_of: Web/API/IndexedDB_API/Using_IndexedDB
original_slug: Web/API/API_IndexedDB/Using_IndexedDB
---
<p>IndexedDB est un moyen de stocker des données de manière persistante dans un navigateur. Cela vous laisse créer des applications web avec de riches possibilités de requêtes indépendamment de la disponibilité du réseau puisque vos applications peuvent fonctionner en ligne ou hors-ligne.</p>

<h2 id="À_propos_de_ce_document">À propos de ce document</h2>

<p>Ce tutoriel vous guide à travers l'utilisation de l'API asynchrone de IndexedDB. Si vous n'êtes pas familier avec le principe de IndexedDB, vous devriez d'abord lire <a href="/fr/docs/Web/API/Indexeddb_API/Basic_Concepts_Behind_IndexedDB">les concepts basiques d'IndexedDB</a>.</p>

<p>Pour la documentation de référence sur l'API d'IndexedDB, voyez l'article <a href="/fr/docs/Web/API/Indexeddb_API">IndexedDB</a> et ses sous-parties, qui détaille les types d'objets utilisés par IndexedDB, ainsi que les méthodes sur l'API asynchrone (l'API synchrone a été retirée de la spécification).</p>

<h2 id="pattern">Modèle de base</h2>

<p>Le modèle de base qu'IndexedDB utilise est le suivant :</p>

<ol>
 <li>Ouvrir une base de données.</li>
 <li>Créer un objet de stockage dans la base de données. </li>
 <li>Démarrer une transaction, et faire des requêtes pour faire quelques opérations sur des bases de données, comme ajouter, ou récupérer des données.</li>
 <li>
  <div>Attendre que l'exécution soit terminée, en écoutant le bon type d'événement DOM.</div>
 </li>
 <li>
  <div>Faire quelque chose avec les résultats (qui peuvent être trouvés dans l'objet de la requête).</div>
 </li>
</ol>

<p>Maintenant que nous avons ces grands concepts en poche, nous pouvons voir des choses plus concrètes.</p>

<h2 id="open">Créer et structurer l'objet de stockage</h2>

<p>Étant donné que la spécification évolue encore, les implémentations actuelles de IndexedDB se cachent sous les préfixes du navigateur. Les fournisseurs de navigateurs peuvent avoir des implémentations différentes de l'API IndexedDB standard jusqu'à ce que la spécification se soit solidifiée. Mais une fois qu'un consensus est atteint sur la norme, les fournisseurs l'implémentent sans les balises de préfixe. Actuellement, certaines implémentations ont supprimé le préfixe : Internet Explorer 10, Firefox 16, Chrome 24. Lorsqu'ils utilisent un préfixe, les navigateurs basés sur Gecko utilisent le préfixe  <code>moz</code>, tandis que les navigateurs WebKit utilisent le préfixe  <code>webkit</code>.</p>

<h3 id="Utiliser_une_version_expérimentale_dIndexedDB">Utiliser une version expérimentale d'IndexedDB</h3>

<p>Au cas où vous souhaiteriez tester votre code dans des navigateurs qui utilisent toujours un préfixe, vous pouvez utiliser le code suivant :  </p>

<pre class="brush: js">// Sur la ligne suivante, vous devez inclure les préfixes des implémentations que vous souhaitez tester.
window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
// N'UTILISEZ PAS "var indexedDB = ..." si vous n'êtes pas dans une fonction.
// De plus, vous pourriez avoir besoin de réferences à des objets window.IDB*:
window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction;
window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange
// (Mozilla n'a jamais préfixé ces objets, donc nous n'avons pas besoin de window.mozIDB*)</pre>

<p>Faites attention aux implémentations qui utilisent un préfixe ; elles peuvent être boguées, incomplètes, voire suivre une ancienne version de la spécification. Il n'est donc pas recommandé d'utiliser en production. Il serait préférable de ne pas supporter ces navigateurs :</p>

<pre class="brush: js">if (!window.indexedDB) {
    window.alert("Votre navigateur ne supporte pas une version stable d'IndexedDB. Quelques fonctionnalités ne seront pas disponibles.")
}
</pre>

<h3 id="Ouvrir_une_base_de_données">Ouvrir une base de données</h3>

<p>On commence l'ensemble du processus comme ceci :</p>

<pre class="brush: js">// Ouvrons notre première base
var request = window.indexedDB.open("MyTestDatabase", 3);
</pre>

<p>Vous avez vu ? Ouvrir une base de données est comme n'importe quelle autre opération — vous avez juste à le "demander".</p>

<p>La requête "open" n'ouvre pas la base de données ni ne démarre une transaction aussitôt. L'appel de la fonction <code>open()</code> retourne un objet <a href="/en-US/docs/IndexedDB/IDBOpenDBRequest"><code>IDBOpenDBRequest</code></a> avec un résultat  (success) ou une valeur d'erreur qui permet de la gérer comme un évènement. La plupart des autres fonctions asynchrones dans IndexedDB fonctionnent de la même façon ; Elles retournent un objet <a href="/en-US/docs/IndexedDB/IDBRequest"><code>IDBRequest</code></a> avec le résultat ou une erreur. Le résultat de la fonction "open" est une instance de <code><a href="/en-US/docs/IndexedDB/IDBDatabase">IDBDatabase</a></code>.</p>

<p>Le second paramètre de la méthode open est la version de la base de données. La version de la base détermine le schéma de celle-ci — Les objets stockés dans la base de données et leur structure. Si la base de données n'existe pas déjà, elle est créée via l'opération <code>open()</code>, puis, un événement <code>onupgradeneeded</code> est déclenché et vous créez le schéma de la base dans le gestionnaire pour cet événement. Si la base de données existe, mais que vous spécifiez un numéro de version plus élevé, un événement <code>onupgradeneeded</code> est déclenché  immédiatement, vous permettant de mettre à jour le schéma dans son gestionnaire – plus d'informations dans <a href="#Updating_the_version_of_the_database">Updating the version of the database</a> plus bas et la page référence {{ domxref("IDBFactory.open") }}.</p>

<div class="warning">
<p><strong>Attention :</strong> Le numéro de version est un nombre "<code>unsigned long long</code>" ce qui signifie qu'il peut s'agir d'un entier très grand. Cela veut également dire que vous ne pouvez pas utiliser de réél, sinon, il sera converti au nombre entier le plus proche (inférieur) et la transaction peut ne pas démarrer ou ne pas déclencher l'événement <code>upgradeneeded</code>. Par exemple, n'utilisez pas 2.4 comme un numéro de version :<br>
 <code>var request = indexedDB.open("MyTestDatabase", 2.4); // Ne faites pas ça, même si la version sera arrondie à 2</code></p>
</div>

<h4 id="Générer_des_gestionnaires">Générer des gestionnaires</h4>

<p>La première chose que vous ferez avec la plupart des requêtes que vous générerez sera d'ajouter des gestionnaires de succès ou d'erreurs :</p>

<pre class="brush: js">request.onerror = function(event) {
  // Faire quelque chose avec request.errorCode !
};
request.onsuccess = function(event) {
  // Faire quelque chose avec request.result !
};</pre>

<p>Laquelle de ces deux fonctions, <code>onsuccess()</code> or <code>onerror()</code>, sera appelée ? Si tout se passe bien, un évènement success (qui est un évènement DOM dont la propriété <code>type</code> est à <code>"success"</code>) est déclenché avec <code>request</code> comme cible. Une fois déclenché, la fonction <code>onsuccess()</code> de <code>request</code> est lancée avec l'évènement success comme argument. S’il y avait un quelconque problème, un évènement erreur (qui est un évènement DOM dont la propriété <code>type</code> est définie à <code>"error"</code>) est lancée dans <code>request</code>. Cela déclenche la fonction <code><code>onerror()</code></code> avec l'évènement d'erreur comme argument.</p>

<p>L'API IndexedDB est conçue pour minimiser le recours à la gestion des erreurs, donc vous ne serez pas amené à voir beaucoup d'évènements erreurs (du moins, pas tant que vous utilisez l'API !). Cependant, dans le cas d'une ouverture de base de données, il y a quelques conditions qui génèrent des évènements d'erreurs. Le problème le plus courant est que l'utilisateur a décidé d'interdire l'accès à la création de base de données. Un des principaux objectifs d’IndexedDB est de permettre un stockage important de données pour l'utilisation hors-ligne. (Pour en savoir plus sur la capacité de stockage de chaque navigateur, voyez <a href="/en/IndexedDB#Storage_limits">Storage limits</a>).</p>

<p>Évidemment, les navigateurs ne peuvent permettre qu'une publicité en ligne ou un site malicieux pollue votre ordinateur, donc ils informent l’utilisateur la première fois qu'une application web tente d'ouvrir un espace de stockage IndexedDB. L'utilisateur peut choisir de permettre ou refuser l'accès. En ce qui concerne l’utilisation d’IndexedDB en mode privé, les données restent en mémoire jusqu’à ce que la session privée soit close (Navigation privée pour Firefox et mode Incognito pour Chrome, mais dans Firefox, cela <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=781982">n'est pas encore implémenté</a> depuis novembre 2015, aussi vous ne pouvez pas utiliser IndexedDB dans le mode privé de Firefo du tout).</p>

<p>Maintenant, en admettant qu’un utilisateur ait accepté la création d'une base, et que vous receviez un évènement "success" qui déclenche le callback <em>(rappel)</em> "success" ; que se passe-il après ? La requête a génèré un appel à <code>indexedDB.open()</code>, donc <code>request.result</code> est une instance de <code>IDBDatabase</code>, et vous voulez garder en mémoire cela pour plus tard. Votre code devrait ressembler à ceci :</p>

<pre class="brush: js">var db;
var request = indexedDB.open("MyTestDatabase");
request.onerror = function(event) {
  alert("Pourquoi ne permettez-vous pas à ma web app d'utiliser IndexedDB?!");
};
request.onsuccess = function(event) {
  db = event.target.result;
};
</pre>

<h4 id="Gérer_les_erreurs">Gérer les erreurs</h4>

<p>Comme mentionné ci-dessus, les évènements d’erreur génèrent des info-bulles. Ils  sont rattachés à la requête qui a généré l’erreur, puis la bulle de l'évènement est transmis à la transaction, et enfin à l'objet de la base de données. Si vous souhaitez éviter d'ajouter un gestionnaire d'erreurs à chaque requête, vous pouvez en ajouter un unique à l'objet de la base de donnée, de cette manière :</p>

<pre class="brush: js">db.onerror = function(event) {
  // Gestionnaire d'erreur générique pour toutes les erreurs de requêtes de cette base
  alert("Database error: " + event.target.errorCode);
};
</pre>

<p>Une des erreurs courantes possibles lorsqu'on ouvre une base de données, c'est <code>VER_ERR</code>. Celle-ci indique que la version de la base de données stockée sur le disque est <em>supérieure </em>à la version que vous êtes en train d'essayer d'ouvrir. C'est un cas qui doit toujours être pris en considération par le gestionnaire d'erreurs.</p>

<h3 id="Créer_ou_mettre_à_jour_une_version_de_base_de_données">Créer ou mettre à jour une version de base de données</h3>

<p>Lorsque vous créez une nouvelle base de données, ou que vous augmentez le numéro de version d'une base existante (en spécifiant un numéro de version supérieur à celui que vous aviez auparavant, lors de {{ anch("Ouvrir une base de données") }}), l'évènement <code>onupgradeneeded</code> sera déclenché et un objet <code>IDBVersionChangeEvent</code> sera passé à un évènement <code>onversionchange</code> dans <code>request.result</code> (la variable <code>db</code> dans l'exemple). Dans le gestionnaire d’évènement <code>upgradeneeded</code>, vous devez créer les objets de stockage requis pour cette version de base :</p>

<pre class="brush:js;">// Cet évènement est seulement implémenté dans des navigateurs récents
request.onupgradeneeded = function(event) {
  var db = event.target.result;

  // Crée un objet de stockage pour cette base de données
  var objectStore = db.createObjectStore("name", { keyPath: "myKey" });
};</pre>

<p>Dans ce cas, la base de données disposera aussitôt des objets de stockage de la version précédente de la base, donc vous n’aurez pas à créer de nouveau ces objets de stockage. Vous aurez seulement besoin de créer de nouveaux objets de stockage, ou d'en supprimer de la version précédente si vous n'en avez plus besoin. Si vous avez besoin de changer un objet de stockage existant  (par exemple, pour changer la <code>keyPath</code>), alors vous devez supprimer l’ancien objet de stockage et le créer à nouveau avec les nouveaux paramètres. Notez que ceci supprimera les informations dans l'objet de stockage ! Si vous avez besoin de sauvegarder ces informations, vous devez les lire et les sauvegarder quelque part avant de mettre à jour la base de données.</p>

<p>Essayer de créer un objet de stockage avec un nom déjà existant (ou essayer de supprimer un objet de stockage avec un nom qui n'existe pas encore) renverra une erreur. </p>

<p>Si l'évènement <code>onupgradeneeded</code> quitte avec succès, le gestionnaire <code>onsuccess</code> de la requête d'ouverture de la base de données sera déclenché. </p>

<p>Blink/Webkit supporte la version courante de la spécification, telle que livrée dans Chrome 23+ et Opera 17+ ; IE10+ également. Les autres implémentations plus anciennes ne prennent pas en charge <code>indexedDB.open(name, version).onupgradeneeded</code>. Pour plus d'informations sur la mise à jour de version de base de données sur les anciens Webkit/Blink, référez vous à <a href="/en/IndexedDB/IDBDatabase#setVersion%28%29_.0A.0ADeprecated">IDBDatabase reference article</a>.</p>

<h3 id="Structurer_la_base_de_données">Structurer la base de données</h3>

<p>Maintenant, structurons la base de données. IndexedDB utilise des objets de stockage plutôt que des tableaux, et une seule base de données peut contenir un nombre quelconque d'objets de stockage. Chaque fois qu'une valeur est stockée dans un objet de stockage, elle est associée à une clé. Il y a différentes manières pour une clé d'être définie, selon que l'objet de stockage utilise un <a href="/en/IndexedDB#gloss_key_path">key path</a> <em>(chemin de clé)</em> ou un <a href="/en/IndexedDB#gloss_key_generator">key generator</a> <em>(générateur de clé)</em>.</p>

<p>Le tableau suivant montre les différentes manières d'attribuer des clés.</p>

<table class="standard-table">
 <thead>
  <tr>
   <th scope="col">Key Path <em>chemin de clé </em>(<code>keyPath</code>)</th>
   <th scope="col">Key Generator <em>générateur de clé </em>(<code>autoIncrement</code>)</th>
   <th scope="col">Description</th>
  </tr>
 </thead>
 <tbody>
  <tr>
   <td>Non</td>
   <td>Non</td>
   <td>L'objet de stockage peut contenir n'importe quel type de valeur, même des valeurs primitives comme des nombres ou des chaînes de caractères. Vous devez fournir un argument clé séparé chaque fois que vous souhaitez ajouter une nouvelle valeur.</td>
  </tr>
  <tr>
   <td>Oui</td>
   <td>Non</td>
   <td>L'objet de stockage peut contenir des objets JavaScript. Les objets doivent avoir une propriété qui a le même nom que le key path.</td>
  </tr>
  <tr>
   <td>Non</td>
   <td>Oui</td>
   <td>L'objet de stockage peut contenir n'importe quel type de valeur. La clé est générée pour vous automatiquement, ou vous pouvez fournir un argument  clé séparé si vous voulez utiliser une clé spécifique.</td>
  </tr>
  <tr>
   <td>Oui</td>
   <td>Oui</td>
   <td>L'objet de stockage peut contenir des objets JavaScript. Normalement, une clé est générée, et sa valeur est stockée dans l'objet dans une propriété avec le même nom que le key path. Cependant, si une telle propriété existe, sa valeur est utilisée en tant que clé, plutôt que la génération d'une nouvelle clé.</td>
  </tr>
 </tbody>
</table>

<p>Vous pouvez aussi créer des index sur un objet de stockage, à condition que l'objet de stockage contienne des objets, et non des primitives. Un index vous permet de consulter les valeurs stockées dans un objet de stockage en utilisant la valeur d'une propriété de l'objet stocké, plutôt que la clé de l'objet.</p>

<p>En outre, les index ont la capacité d'appliquer des contraintes simples sur les données stockées. En paramétrant l'option <code>unique</code> lorsque l'on crée un index, ce dernier fait que deux objets ne peuvent être enregistrés en ayant la même valeur pour le chemin de clé de l'index. Par exemple, si vous avez un objet de stockage qui contient un ensemble de personnes, et que vous voulez vous assurer que deux personnes n’aient pas la même adresse de courriel, vous pouvez utiliser un index avec le paramètre <code>unique</code> à <code>true</code>.</p>

<p>Cela peut sembler confus, mais ce simple exemple devrait illustrer ces concepts. D'abord, nous définissons quelques données client à utiliser dans notre exemple :</p>

<pre class="brush: js">// Voici à quoi ressemblent nos données client.
const customerData = [
  { ssn: "444-44-4444", name: "Bill", age: 35, email: "bill@company.com" },
  { ssn: "555-55-5555", name: "Donna", age: 32, email: "donna@home.org" }
];</pre>

<p>Bien sûr, vous n'utiliseriez pas le numéro de sécurité sociale comme clé primaire dans une table clients parce que tout le monde n'a pas de numéro de sécurité sociale, et vous pourriez stocker leur date de naissance au lieu de leur âge, mais laissons ces choix non pertinents pour des raisons de commodité et continuons.</p>

<p>Maintenant, voyons la création d'une base de données pour stocker ces données :</p>

<pre class="brush: js">const dbName = "the_name";

var request = indexedDB.open(dbName, 2);

request.onerror = function(event) {
  // Gestion des erreurs.
};
request.onupgradeneeded = function(event) {
  var db = event.target.result;

  // Créer un objet de stockage qui contient les informations de nos clients.
  // Nous allons utiliser "ssn" en tant que clé parce qu'il est garanti d'être
  // unique - du moins, c'est ce qu'on en disait au lancement.
  var objectStore = db.createObjectStore("customers", { keyPath: "ssn" });

  // Créer un index pour rechercher les clients par leur nom. Nous pourrions
  // avoir des doublons (homonymes), alors on n'utilise pas d'index unique.
  objectStore.createIndex("name", "name", { unique: false });

  // Créer un index pour rechercher les clients par leur adresse courriel. Nous voulons nous
  // assurer que deux clients n'auront pas la même, donc nous utilisons un index unique.
  objectStore.createIndex("email", "email", { unique: true });

  // Utiliser la transaction "oncomplete" pour être sûr que la création de l'objet de stockage
  // est terminée avant d'ajouter des données dedans.
  objectStore.transaction.oncomplete = function(event) {
    // Stocker les valeurs dans le nouvel objet de stockage.
    var customerObjectStore = db.transaction("customers", "readwrite").objectStore("customers");
    for (var i in customerData) {
      customerObjectStore.add(customerData[i]);
    }
  }
};</pre>

<div>Comme indiqué précédemment, <code>onupgradeneeded</code> est le seul endroit où vous pouvez modifier la structure de la base de données. Dans cette méthode, vous pouvez créer et supprimer des objets de stockage, construire et supprimer des index.</div>

<div></div>

<p>Les objets de stockage sont créés avec un simple appel à <code>createObjectStore()</code>. La méthode prend le nom du stockage et un paramètre de type objet. Même si les paramètres sont optionnels, ils vous laissent définir d'importantes propriétés et redéfinir le type d'un objet de stockage que vous voulez créer. Dans notre cas, nous avons demandé un objet de stockage nommé "customers" et défini un <code>keyPath</code>, qui est la propriété rendant unique un objet individuel dans le stockage. Cette propriété dans l'exemple est "ssn" puisqu'un numéro de sécurité sociale est garanti unique. "ssn" doit être présent sur chaque objet stocké dans <code>objectStore</code>.</p>

<p>Nous avons aussi demandé un index nommé "name" qui examine la propriété <code>name</code> dans les objets stockés. Comme avec<code> createObjectStore()</code>, <code>createIndex()</code> prend un paramètre de type objet facultatif (<code>options</code>) qui définit le type d’index à créer. Ajouter des objets qui n’auront pas de propriété <code>name</code> fonctionnera, mais ces objets n'apparaîtront pas dans l'index "name".</p>

<p>Nous pouvons récupérer les objets client stockés, en utilisant directement leur <code>ssn</code> dans l'objet de stockage, ou en utilisant leur nom via l’index <code>name</code>. Pour en savoir plus sur ce fonctionnement, se référer à la section <a href="/en/IndexedDB/Using_IndexedDB#Using_an_index">utiliser un index</a>.</p>

<h3 id="Utiliser_le_générateur_de_clés">Utiliser le générateur de clés</h3>

<p>Paramétrer un marqueur <code>autoIncrement</code> lorsque l'on crée un objet de stockage activera le générateur de clés pour cet objet de stockage. Par défault, ce marqueur n'est pas défini.</p>

<p>Avec la générateur de clés, une clé sera générée automatiquement lorsque vous ajoutez une valeur dans un objet de stockage. Le compteur initial pour la génération de clés est toujours défini à 1 lorsque l'objet de stockage est créé pour la première fois. Fondamentalement, une nouvelle clé auto-générée sera incrémentée de 1 par rapport à la précédente. Le nombre courant d'un générateur de clé ne décroit jamais, à moins qu'un résultat d'opération sur la base soit annulé, par exemple, l'abandon d'une transaction sur la base. En conséquence, supprimer un enregistrement, voire l'ensemble des enregistrements d'un objet de stockage n'affecte jamais le générateur de clés d'un objet de stockage.</p>

<p>Nous pouvons créer un autre objet de stockage avec un générateur de clés comme ci-dessous :</p>

<pre class="brush: js">// Ouverture d'indexedDB.
var request = indexedDB.open(dbName, 3);

request.onupgradeneeded = function (event) {

    var db = event.target.result;

    // Création d'un autre objet appelé "names" avec l'option autoIncrement définie à true.
    var objStore = db.createObjectStore("names", { autoIncrement : true });

    // Puisque l'objet "names" a un générateur de clés, la clé pour la valeur name est générée automatiquement.
    // Les enregistrements ajoutés ressembleront à ceci :
    // key : 1 =&gt; value : "Bill"
    // key : 2 =&gt; value : "Donna"
    for (var i in customerData) {
        objStore.add(customerData[i].name);
    }
}</pre>

<p>Pour plus de détails sur le générateur de clés, voyez <a href="http://www.w3.org/TR/IndexedDB/#key-generator-concept">"W3C Key Generators"</a>.</p>

<h2 id="Ajouter_récupérer_et_supprimer_des_données">Ajouter, récupérer et supprimer des données</h2>

<p>Avant de faire quoi que ce soit avec votre nouvelle base de données, vous aurez besoin de démarrer une transaction. Les transactions viennent de l'objet base de données, et vous devez spécifier sur quel objet vous souhaitez faire pointer la transaction. Une fois dans la transaction, vous pouvez accéder à l'objet de stockage qui contient vos données et faire vos requêtes. Puis, vous devez décider si vous allez appliquer des changements à la base de données, ou si vous avez juste besoin de la lire. Les transactions disposent de trois modes disponibles: <code>readonly</code> <em>(lecture seule)</em>, <code>readwrite</code> <em>(lecture/écriture)</em>, et <code>versionchange</code> <em>(changement de version)</em>.</p>

<p>Pour changer le "schéma" ou la structure de la base de données — qui implique de créer ou supprimer des objets de stockage ou des index — la transaction doit être en mode <code>versionchange</code>. Cette transaction est ouverte en appelant la méthode {{domxref("IDBFactory.open")}}  avec une <code>version</code> spécifiée. (Dans les navigateurs WebKit, qui n'ont pas implémenté la dernière spécification, la méthode {{domxref("IDBFactory.open")}} prend seulement un paramètre, le <code>nom</code> de la base de données ; Vous devez donc appeler {{domxref("IDBVersionChangeRequest.setVersion")}} pour établir la transaction <code>versionchange</code>.)</p>

<p>Pour lire les enregistrements d'un objet de stockage existant, la transaction peut être en mode <code>readonly</code>ou <code>readwrite</code>. Pour appliquer des changements à un objet de stockage existant, la transaction doit être en mode <code>readwrite</code>. Vous démarrez ces transactions avec {{domxref("IDBDatabase.transaction")}}. La méthode accepte deux paramètres : les <code>storeNames</code> (la portée, définie comme un tableau des objets de stockage auxquels vous souhaitez accéder) et le <code>mode</code> (<code>readonly</code> ou <code>readwrite</code>) pour la transaction. La méthode retourne un objet de transaction contenant la méthode {{domxref("IDBIndex.objectStore")}}, que vous utilisez pour accéder à votre objet de stockage. Par défaut, lorsqu'aucun mode n'est spécifié, les transactions démarrent en mode <code>readonly</code>.</p>

<div class="note">
<p><strong>Note :</strong> À partir de Firefox 40, les transactions IndexedDB ont des garanties de durabilité relachées afin d'augmenter les performances (voir {{Bug("1112702")}}.) Auparavant, lors d'une transaction <code>readwrite</code> {{domxref("IDBTransaction.oncomplete")}} était déclenché seulement lorsque les données étaient garanties pour une écriture sur le disque. Dans Firefox 40+ l'évènement <code>complete</code> est déclenché une fois que l'OS a autorisé l'écriture de données, mais potentiellement avant que les données soient réellement écrites sur le disque. L'évènement <code>complete</code> peut ainsi être livré plus vite qu'avant, cependant, il existe un petit risque que l'ensemble de la transaction soit perdu si l'OS s'effondre ou si un problème électrique survient avant que les données soient écrites. Comme de tels évènements catastrophiques sont rares, la plupart des utilisateurs n'ont pas à s'en soucier. Si vous devez vous assurer de la durabilité pour quelconque raison que ce soit (par exemple, vous stockez des données critiques qui ne peuvent être recalculées plus tard) vous pouvez forcer une transaction à écrire sur le disque avant que l'évènement <code>complete</code> ne soit délivré en créant une transaction utilisant un mode expérimental (non-standard) <code>readwriteflush</code>  (se référer à {{domxref("IDBDatabase.transaction")}}.</p>
</div>

<p>Vous pouvez accélérer l'accès à vos données en utilisant le bon mode et la bonne portée dans la transaction. Voici deux astuces :</p>

<ul>
 <li>Lorsque vous définissez la portée, spécifiez uniquement les objets de stockage dont vous avez besoin. De cette manière, vous pouvez exécuter plusieurs transactions simultanément sans qu'elles se chevauchent.</li>
 <li>Spécifier le mode <code>readwrite</code> pour une transaction seulement lorsque c'est nécessaire. Vous pouvez exécuter simulaténement plusieurs transactions <code>readonly</code> avec chevauchements, mais vous ne pouvez avoir qu'une seule transaction <code>readwrite</code> dans un objet de stockage. Pour en savoir plus, regardez la définition des <dfn><a href="/en-US/docs/IndexedDB/Basic_Concepts_Behind_IndexedDB#Database">transactions</a></dfn> dans l'article des <a href="/en-US/docs/IndexedDB/Basic_Concepts_Behind_IndexedDB">concepts de base</a>.</li>
</ul>

<h3 id="Ajouter_des_données_dans_la_base_de_données">Ajouter des données dans la base de données</h3>

<p>Si vous venez juste de créer une base de données, alors vous souhaitez probablement écrire dedans. Voici comment ça se passe :</p>

<pre class="brush:js;">var transaction = db.transaction(["customers"], "readwrite");
// Note: Les anciennes implémentations utilisent la constante dépréciée IDBTransaction.READ_WRITE au lieu de "readwrite".
// Au cas où vous souhaitiez mettre en oeuvre ces implémentations, vous pouvez écrire :
// var transaction = db.transaction(["customers"], IDBTransaction.READ_WRITE);</pre>

<p>La fonction <code>transaction()</code> prend deux arguments (bien que l'un d'eux soit facultatif) et retourne un objet transaction. Le premier argument est une liste d'objets de stockage que la transaction va traiter. Vous pouvez passer un tableau vide si vous voulez que la transaction traite l'ensemble des objets de stockage, mais ne le faites pas, parce que la spécification indique qu'un tableau vide devrait générer une InvalidAccessError. Si vous ne spécifiez rien pour le deuxième argument, vous démarrerez une transaction "read-only" <em>(lecture seule)</em> . Si vous souhaitez aussi écrire, vous devrez passer l'option <code>"readwrite"</code> <em>(lecture/écriture)</em>.</p>

<p>Maintenant que vous avez une transaction, vous devez comprendre sa durée de vie. Les transactions sont étroitement liées à la boucle de l'évènement. Si vous établissez une transaction et si vous sortez de la boucle d'évènements sans l'utiliser, alors la transaction deviendra inactive. La seule manière de garder la transaction active est d'y insérer une requête. Lorsque la requête est terminée, vous obtenez un évènement DOM, et en supposant que la requête ait réussi, vous avez une autre opportunité d'étendre la transaction durant ce "callback" <em>(rappel)</em>. Si vous sortez de la boucle d'évènements sans étendre la transaction, alors elle devient inactive, etc… Tant qu'il reste des demandes en attente, la transaction reste active. La durée de vie des transactions est vraiment très simple, mais cela peut prendre un peu de temps de la maîtriser. Quelques exemples supplémentaires aideront. Si vous commencez à voir des codes d'erreur <code>TRANSACTION_INACTIVE_ERR</code>, alors vous avez raté quelque chose.</p>

<p>Les transactions peuvent recevoir des évènements DOM de trois types : <code>error</code> <em>(erreur)</em>, <code>abort</code> <em>(abandonnée)</em> et <code>complete</code> <em>(terminée)</em>. Nous avons déjà parlé du fait que les <code>error</code> créent des bulles, ainsi une transaction peut recevoir des évènements d'erreur venant de n'importe quelle requête l'ayant généré. Un point plus subtil ici, c'est que le comportement par défaut d'une erreur est d'abandonner la transaction là où elle a eu lieu. A moins que vous gériez l’erreur en appelant d'abord <code>stopPropagation()</code> sur l’évènement erreur, puis que vous fassiez quelque chose d'autre, la transaction complète sera annulée. Cette conception vous oblige à réfléchir et gérer les erreurs, mais vous pouvez toujours ajouter un gestionnaire d'erreurs "fourre-tout" à la base de données si la gestion d'erreurs fines est trop lourde. Si vous ne gérez pas un évènement d'erreur, ou si vous appelez <code>abort()</code> sur la transaction, alors la transaction est annulée et un évènement <code>abort</code> est lancé sur la transaction. Sinon, une fois que toutes les demandes en instance sont terminées, vous recevez un évènement <code>complete</code>. Si vous faites beaucoup d'opérations sur les bases de données, alors suivre la transaction plutôt que les requêtes individuelles, peut certainement vous aider.</p>

<p>Maintenant que vous avons une transaction, nous avons besoin de récupérer l'objet de stockage de celle-ci. Les transactions vous permettent seulement d'avoir l'objet de stockage que vous avez spécifié lors de la création de la transaction. Puis, vous pouvez ajouter toutes les données dont vous avez besoin.</p>

<pre class="brush: js">// Faire quelque chose lorsque toutes les données sont ajoutées à la base de données.
transaction.oncomplete = function(event) {
  alert("All done!");
};

transaction.onerror = function(event) {
  // N'oubliez pas de gérer les erreurs !
};

var objectStore = transaction.objectStore("customers");
for (var i in customerData) {
  var request = objectStore.add(customerData[i]);
  request.onsuccess = function(event) {
    // event.target.result == customerData[i].ssn;
  };
}</pre>

<div>La méthode <code>result</code> d’une requête venant d'un appel à <code>add()</code> est la clé de la valeur qui vient d'être ajoutée. Dans ce cas, ce devrait être équivalent à la propriété <code>ssn</code> de l'objet qui vient d'être ajouté, puisque l'objet de stockage utilise la propriété <code>ssn</code> pour le key path. Notez que la fonction <code>add()</code> requiert qu'aucun objet déjà présent dans la base ait la même clé. Si vous essayez de modifier une entrée existante, ou si vous ne vous en occupez pas, vous pouvez utiliser la fonction <code>put()</code>, comme montré plus loin dans la section {{ anch("Updating an entry in the database") }}.</div>

<div></div>

<h3 id="Supprimer_des_données_dans_la_base_de_données">Supprimer des données dans la base de données</h3>

<p>Supprimer des données est très similaire :</p>

<pre class="brush: js">var request = db.transaction(["customers"], "readwrite")
                .objectStore("customers")
                .delete("444-44-4444");
request.onsuccess = function(event) {
  // c'est supprimé !
};</pre>

<h3 id="Récupérer_des_données_de_la_base_de_données">Récupérer des données de la base de données</h3>

<p>Maintenant que la base de données dispose de quelques informations, vous pouvez les récupérer de plusieurs façons. D'abord, la plus simple <code>get()</code>. Vous devez fournir une clé pour récupérer la valeur, comme ceci :</p>

<pre class="brush: js">var transaction = db.transaction(["customers"]);
var objectStore = transaction.objectStore("customers");
var request = objectStore.get("444-44-4444");
request.onerror = function(event) {
  // gestion des erreurs!
};
request.onsuccess = function(event) {
  // Faire quelque chose avec request.result !
  alert("Name for SSN 444-44-4444 is " + request.result.name);
};</pre>

<p>Ça fait beaucoup de code pour une "simple" récupération. Voici comment le raccourcir un peu, en supposant que vous gériez les erreurs au niveau de la base de données :</p>

<pre class="brush: js">db.transaction("customers").objectStore("customers").get("444-44-4444").onsuccess = function(event) {
  alert("Name for SSN 444-44-4444 is " + event.target.result.name);
};</pre>

<div>Vous voyez comment ça fonctionne ? Comme il n'y a qu'un seul objet de stockage, vous pouvez éviter de passer une liste d'objets dont vous avez besoin dans votre transaction, et juste passer le nom comme une chaîne de caractères. Aussi, nous faisons seulement une lecture de la base, donc nous n'avons pas besoin d'une transaction <code>"readwrite"</code>. Appeler une <code>transaction()</code> sans spécifier de mode nous donne une transaction <code>"readonly"</code>. Une autre subtilité ici est que nous n'enregistrons pas l'objet de notre requête dans une variable. Comme l’évènement DOM a la requête comme cible, vous pouvez utiliser l'évènement pour récupérer la propriété <code>result</code>.</div>

<div></div>

<p>Vous pouvez accélérer l’accès à vos données  en limitant la portée et le mode de la transaction. Voici deux astuces :</p>

<ul>
 <li>Lors de la définition de la <a href="/fr/docs/IndexedDB/Using_IndexedDB$edit#scope">scope</a> <em>(portée)</em>, spécifiez seulement l’objet de stockage dont vous avez besoin. De cette manière, vous pouvez avoir de multiples opérations simultanées sans qu’elles se chevauchent.</li>
 <li>Spécifier une transaction en mode readwrite uniquement lorsque c’est nécessaire. Vous pouvez avoir de multiples opérations simultanées en lecture seule, mais vous ne pouvez avoir qu’une transaction "readwrite" <em>(lecture/écriture)</em> sur un objet de stockage. Pour en savoir plus, voir la définition relative aux <a href="/en-US/docs/IndexedDB/Basic_Concepts_Behind_IndexedDB#gloss_transaction">transactions in the Basic Concepts article</a>.</li>
</ul>

<h3 id="Mettre_à_jour_une_entrée_dans_la_base_de_données">Mettre à jour une entrée dans la base de données</h3>

<p>Maintenant que nous avons récupéréré quelques données, les mettre à jour et en insérer est assez simple. Mettons à jour l’exemple précédent :</p>

<pre class="brush: js">var objectStore = db.transaction(["customers"], "readwrite").objectStore("customers");
var request = objectStore.get("444-44-4444");
request.onerror = function(event) {
  // Gestion des erreurs!
};
request.onsuccess = function(event) {
  // On récupère l'ancienne valeur que nous souhaitons mettre à jour
  var data = request.result;

  // On met à jour ce(s) valeur(s) dans l'objet
  data.age = 42;

  // Et on remet cet objet à jour dans la base
  var requestUpdate = objectStore.put(data);
   requestUpdate.onerror = function(event) {
     // Faire quelque chose avec l’erreur
   };
   requestUpdate.onsuccess = function(event) {
     // Succès - la donnée est mise à jour !
   };
};</pre>

<div>Ici, nous avons créé une variable <code>objectStore</code> et nous avons recherché un enregistrement d’un client, identifié par la valeur ssn (<code>444-44-4444</code>). Nous avons ensuite mis le résultat dans une variable (<code>data</code>), mis à jour la propriété <code>age</code> de cet objet, puis créé une deuxième requête (<code>requestUpdate</code>) pour mettre l'enregistrement du client dans l'<code>objectStore</code>, en écrasant la valeur précédente.</div>

<div class="note">
<p><strong>Note :</strong> dans ce cas, nous avons eu à spécifier une transaction <code>readwrite</code> puisque nous voulions écrire dans la base, et pas seulement la lire.</p>
</div>

<h3 id="Utiliser_un_curseur">Utiliser un curseur</h3>

<p>Utiliser <code>get()</code> nécessite de connaître la clé que vous souhaitez récupérer. Si vous voulez parcourir toutes les valeurs de l’objet de stockage, alors vous devez utiliser un curseur. Voici comment ça marche :</p>

<pre class="brush: js">var objectStore = db.transaction("customers").objectStore("customers");

objectStore.openCursor().onsuccess = function(event) {
  var cursor = event.target.result;
  if (cursor) {
    alert("Name for SSN " + cursor.key + " is " + cursor.value.name);
    cursor.continue();
  }
  else {
    alert("No more entries!");
  }
};</pre>

<p>La fonction <code>openCursor()</code> prend en compte plusieurs arguments. En premier, vous pouvez spécifier une plage de résultats à récupérer en utilisant un objet  "key range" que nous allons voir dans une minute. En deuxième, vous pouvez spécifier la direction vers laquelle vous souhaitez itérer. Dans l’exemple ci-dessus, nous avons itéré tous les objets dans l’ordre ascendant. Le "callback" <em>(rappel)</em> de réussite pour les curseurs est un peu spécial. L'objet cursor lui-même est le <code>result</code> <em>(résutat)</em> de la requête (au-dessus, nous utilisons le raccourci <code>event.target.result</code>). Puis la clé et valeur courante peuvent être trouvées dans les propriétés <code>key</code><em>(clé)</em>  et <code>value</code> <em>(valeur)</em> de l’objet cursor. Si vous souhaitez continuer, vous devez appeler <code>continue()</code> sur le curseur. Lorsque vous avez atteint la fin des données (ou s’il n’y a plus d’entrées qui correspondent à votre requête <code>openCursor()</code> ) , vous aurez toujours votre callback  success, mais la propriété <code>result</code> sera <code>undefined</code>.</p>

<p>Une utilisation classique avec les curseurs est de récupérer tous les objets dans un objet de stockage et de les mettre dans un tableau, comme ceci :</p>

<pre class="brush: js">var customers = [];

objectStore.openCursor().onsuccess = function(event) {
  var cursor = event.target.result;
  if (cursor) {
    customers.push(cursor.value);
    cursor.continue();
  }
  else {
    alert("Got all customers: " + customers);
  }
};</pre>

<div class="note">
<p><strong>Note :</strong> Mozilla a aussi implémenté <code>getAll()</code> pour gérer ce cas (et <code>getAllKeys()</code>, qui est actuellement caché derrière la préférence <code>dom.indexedDB.experimental</code>  dans about:config) . ceux-ci ne font pas partie d' IndexedDB standard, et peuvent disparaître dans le futur. Nous les avons inclus partceque nous pensons qu'ils sont utiles. Le code suivant fait exactement la même chose que ci-dessus :</p>

<pre class="brush: js">objectStore.getAll().onsuccess = function(event) {
  alert("Got all customers: " + event.target.result);
};</pre>

<p>Il y a un coût de performance associé avec la recherche de la propriété <code>value</code> du curseur, parce que l'objet est créé paresseusement. Quand vous utilisez <code>getAll()</code> par exemple, Gecko doit créer tous les objets à la fois. Si vous êtes seulement intéressé par la lecture de chaque clé, pour l'instance, il est beaucoup plus efficace d'utiliser un curseur que <code>getAll()</code>. Si vous essayez d'obtenir un tableau de tous les objets d'un objet de stockage, utilisez <code>getAll()</code>.</p>
</div>

<h3 id="Utiliser_un_index">Utiliser un index</h3>

<p>Le stockage des données des clients utilisant le SSN comme clé est logique puisque le SSN identifie un individu unique. (Que ce soit une bonne idée pour la vie privée est une question différente, et en dehors du champ de cet article). Si vous devez rechercher un client par son nom, vous devrez toutefois faire itérer sur toutes les clés SSN dans la base de données jusqu'à ce que vous trouviez la bonne. La recherche de cette manière serait très lente, alors, vous pouvez utiliser un index.</p>

<pre class="brush: js line-numbers">// D'abord, assurez-vous de créer un index dans request.onupgradeneeded:
// objectStore.createIndex("name", "name");
// Autrement, vous obtiendrez une DOMException.

var index = objectStore.index("name");

index.get("Donna").onsuccess = function(event) {
  alert("Donna's SSN is " + event.target.result.ssn);
};</pre>

<p>Le "name" du curseur n'est pas unique, donc il pourrait y avoir plus d'une entrée avec le <code>name</code> attribué à  <code>"Donna"</code>. Dans ce cas, vous obtenez toujours celui qui a la valeur clé la plus basse .</p>

<p>Si vous avez besoin d'accèder à toutes les entrées avec un <code>name</code> donné, vous pouvez utiliser un curseur. Vous pouvez ouvrir deux types différents de curseurs sur les index. Un curseur normal situe la propriété index de l'objet dans l'objet de stockage. Un curseur de clés situe la propriété index des clés utilisées pour stocker l'objet dans l'objet de stockage. Les différences sont illustrées ici :</p>

<pre class="brush: js">// Utilisation d'un curseur normal pour saisir tous les enregistrements des objets client
index.openCursor().onsuccess = function(event) {
  var cursor = event.target.result;
  if (cursor) {
    // cursor.key est un nom, comme "Bill", et cursor.value est l'objet entier.
    alert("Name: " + cursor.key + ", SSN: " + cursor.value.ssn + ", email: " + cursor.value.email);
    cursor.continue();
  }
};

// Utilisation d'un curseur de clés pour saisir les clés des enregistrements des objets client
index.openKeyCursor().onsuccess = function(event) {
  var cursor = event.target.result;
  if (cursor) {
    // cursor.key est un nom, comme "Bill", et cursor.value est le SSN.
    // Pas moyen d'obtenir directement le reste de l'objet stocké .
    alert("Name: " + cursor.key + ", SSN: " + cursor.value);
    cursor.continue();
  }
};</pre>

<h3 id="Spécifier_lintervalle_et_la_direction_du_curseur">Spécifier l'intervalle et la direction du curseur</h3>

<p>Si vous souhaitez limiter l'intervalle de valeurs que vous voyez dans un curseur, vous pouvez utiliser un objet <code>IDBKeyRange</code> et le donner comme premier argument à <code>openCursor()</code> ou <code>openKeyCursor()</code> . Vous pouvez créer un intervalle de clés qui n'autorise qu'une seule clé, ou qui a des limites inférieure et supérieure, ou qui a des bornes inférieure et supérieure. La limite peut être "closed" <em>(fermée)</em> (c'est-à-dire que l'intervalle de clés comprend les valeurs données) ou "open" <em>(ouverte)</em> (c'est-à-dire que la plage de clés n'inclut pas les valeurs données. Voici comment cela fonctionne :</p>

<pre class="brush: js">// Correspond seulement à "Donna"
var singleKeyRange = IDBKeyRange.only("Donna");

// Correspond à n'importe quoi contenant "Bill", y compris "Bill"
var lowerBoundKeyRange = IDBKeyRange.lowerBound("Bill");

// Correspond à n'importe quoi contenant "Bill", mais pas "Bill"
var lowerBoundOpenKeyRange = IDBKeyRange.lowerBound("Bill", true);

// Correspond à n'importe quoi, mais  "Donna" exclus.
var upperBoundOpenKeyRange = IDBKeyRange.upperBound("Donna", true);

// Correspond à n'importe quoi compris entre "Bill" et "Donna", mais "Donna" exclus.
var boundKeyRange = IDBKeyRange.bound("Bill", "Donna", false, true);

// Pour utiliser un des intervalles de clés, placez le en premier argument de openCursor()/openKeyCursor()
index.openCursor(boundKeyRange).onsuccess = function(event) {
  var cursor = event.target.result;
  if (cursor) {
    // Faire quelque chose avec la sélection.
    cursor.continue();
  }
};</pre>

<p>Parfois, vous voudrez peut-être itérer dans l'ordre décroissant plutôt que dans l'ordre croissant (la direction par défaut pour tous les curseurs). Le changement de direction est réalisé en passant <code>prev</code>  à la fonction  <code>openCursor()</code>  antérieure comme second argument :</p>

<pre class="brush: js">objectStore.openCursor(boundKeyRange, "prev").onsuccess = function(event) {
  var cursor = event.target.result;
  if (cursor) {
    // Faire quelque chose avec les entrées.
    cursor.continue();
  }
};</pre>

<p>Si vous souhaitez simplement spécifier un changement de direction, mais ne pas limiter les résultats, vous pouvez simplement passer "null" comme premier argument :</p>

<pre class="brush: js">objectStore.openCursor(null, "prev").onsuccess = function(event) {
  var cursor = event.target.result;
  if (cursor) {
    // Faire quelque chose avec les entrées.
    cursor.continue();
  }
};</pre>

<p>Étant donné que l'index "name" n'est pas unique, il peut y avoir plusieurs entrées où le  <code>name</code>  est le même. Notez qu'une telle situation ne peut pas se produire avec les objets stockés car la clé doit toujours être unique. Si vous souhaitez filtrer les doublons pendant l'itération du curseur sur les index, vous pouvez passer  <code>nextunique</code>  (ou  <code>prevunique</code>  si vous revenez en arrière) comme paramètre de direction. Lorsque nextunique ou prevunique sont utilisés, l'entrée avec la clé la plus basse est toujours celle retournée.</p>

<pre class="brush: js">index.openKeyCursor(null, "nextunique").onsuccess = function(event) {
  var cursor = event.target.result;
  if (cursor) {
    // Faire quelque chose avec les entrées.
    cursor.continue();
  }
};</pre>

<p>Voyez "<a href="/en-US/docs/Web/API/IDBCursor?redirectlocale=en-US&amp;redirectslug=IndexedDB%2FIDBCursor#Constants">IDBCursor Constants</a>" pour les arguments de direction valides.</p>

<h2 id="La_version_change_alors_quune_application_Web_est_ouverte_dans_un_autre_onglet">La version change alors qu'une application Web est ouverte dans un autre onglet</h2>

<p>Lorsque votre application Web change de telle sorte qu'une modification de version est nécessaire pour votre base de données, vous devez considérer ce qui se passe si l'utilisateur a l'ancienne version de votre application ouverte dans un onglet, puis charge la nouvelle version de votre application dans une autre . Lorsque vous appelez  <code>open()</code>  avec une version plus grande que la version actuelle de la base de données, toutes les autres bases de données ouvertes doivent reconnaître explicitement la demande avant de commencer à modifier la base de données (un événement  <code>onblocked</code>  <em>(bloqué)</em> est déclenché jusqu'à ce qu'elles soient fermées ou rechargées). Voici comment cela fonctionne :</p>

<pre class="brush: js">var openReq = mozIndexedDB.open("MyTestDatabase", 2);

openReq.onblocked = function(event) {
  //  Si un autre onglet est chargé avec la base de données, il doit être fermé 
  // avant que nous puissions continuer.
  alert("Veuillez fermer tous les ongles ouverts sur ce site!");
};

openReq.onupgradeneeded = function(event) {
  // Toutes les autres bases de données ont été fermées. Tout régler.
  db.createObjectStore(/* ... */);
  useDatabase(db);
}

openReq.onsuccess = function(event) {
  var db = event.target.result;
  useDatabase(db);
  return;
}

function useDatabase(db) {
  // Assurez-vous d'ajouter un gestionnaire pour être averti si une autre page demande 
  // un changement de version. Nous devons fermer la base de données. 
  // Cela permet à l'autre page de mettre à niveau la base de données. 
  //  Si vous ne le faites pas, la mise à niveau ne se produira que lorsque l'utilisateur fermera l'onglet .
  db.onversionchange = function(event) {
    db.close();
    alert("A new version of this page is ready. Please reload!");
  };

  //  Faire quelque chose avec la base de données .
}</pre>

<p>Vous devriez également écouter les erreurs  <code>VersionError</code>  pour gérer le cas où les applications déjà ouvertes déclencheraient un code conduisant à une nouvelle tentative d'ouverture de la base de données, mais en utilisant une version désuète.</p>

<h2 id="Sécurité">Sécurité</h2>

<p>IndexedDB utilise le principe " same-origin " <em>(même origine)</em>, ce qui signifie qu'il relie le stockage à l'origine du site qui le crée (généralement, c'est le domaine ou le sous-domaine du site), de sorte qu'il ne peut être consulté par aucune autre origine.</p>

<p>Le contenu de la fenêtre de tiers (par exemple le contenu de {{htmlelement("iframe")}}) peut accèder à IndexedDB pour l'origine dans laquelle il est intégré, à moins que le navigateur ne soit configuré pour <a href="https://support.mozilla.org/en-US/kb/disable-third-party-cookies">ne jamais accepter de cookies tiers</a> (voir le {{bug("1147821")}}).</p>

<h2 id="Avertissement_concernant_larrêt_du_navigateur">Avertissement concernant l'arrêt du navigateur</h2>

<p>Lorsque le navigateur s'arrête (parce que l'utilisateur a choisi l'option Quit ou Exit), le disque contenant la base de données est supprimé de manière inattendue ou les permissions sont perdues dans le magasin de base de données, les choses suivantes se produisent :</p>

<ol>
 <li>Chaque transaction sur chaque base de données affectée (ou toutes les bases de données ouvertes, dans le cas de l'arrêt du navigateur) est interrompue avec un  <code>AbortError</code>. L'effet est le même que si {{domxref("IDBTransaction.abort()")}} est appelé sur chaque transaction.</li>
 <li>Une fois toutes les transactions terminées, la connexion à la base de données est fermée .</li>
 <li>Enfin, l'objet {{domxref("IDBDatabase")}} représentant la connexion à la base de données reçoit un évènement {{event("close")}} . Vous pouvez utiliser un gestionnaire d'évènements  {{domxref("IDBDatabase.onclose")}} pour écouter ces évènements, afin de savoir quand une base de données est fermée de façon inattendue .</li>
</ol>

<p>Le comportement décrit ci-dessus est nouveau et n'est disponible que pour les versions de navigateur suivantes : Firefox 50, Google Chrome 31 (approximativement).</p>

<p>Avant ces versions de navigateurs, les transactions étaient interrompues silencieusement et aucun événement {{event ("close")}} n'était déclenché, donc il n'y avait aucun moyen de détecter une fermeture de base de données inattendue.</p>

<p>Étant donné que l'utilisateur peut quitter le navigateur à tout moment, cela signifie que vous ne pouvez pas compter sur une transaction particulière à compléter, et sur les navigateurs plus anciens, vous n'êtes même pas informé quand elles ne sont pas terminées. Il y a plusieurs conséquences à ce comportement.</p>

<p>Tout d'abord, vous devez vous occuper de toujours laisser votre base de données dans un état cohérent à la fin de chaque transaction. Par exemple, supposons que vous utilisiez IndexedDB pour stocker une liste d'éléments que l'utilisateur est autorisé à éditer. Vous enregistrez la liste après l'édition en effaçant l'objet de stockage puis en écrivant la nouvelle liste. Si vous effacez l'objet de stockage dans une transaction et que vous écrivez la nouvelle liste dans une autre transaction, il existe un danger : si le navigateur se ferme après l'effacement mais avant l'écriture, votre base de données est  vide. Pour éviter cela, vous devez combiner l'effacement et l'écriture en une seule transaction.</p>

<p>Ensuite, vous ne devez jamais lier les transactions de base de données pour les événements  unload <em>(déchargement</em>). Si l'événement  unload est déclenché par la fermeture du navigateur, toutes les transactions créées dans le gestionnaire d'événements unload ne seront jamais terminées. Une approche intuitive, pour le maintien de certaines informations dans les sessions du navigateur, est de le lire à partir de la base de données, lorsque le navigateur (ou une page particulière) est ouvert, le mettre à jour à mesure que l'utilisateur interagit avec le navigateur, puis l'enregistrer dans la base de données lorsque le navigateur ( ou page) se ferme. Cependant, cela ne fonctionne pas. Les transactions de la base de données sont créées dans le gestionnaire d'événements unload, mais comme elles sont asynchrones, elles sont interrompues avant qu'elles puissent s'exécuter.</p>

<p>En fait, il n'y a aucun moyen de garantir que les transactions IndexedDB seront terminées, même avec un arrêt normal du navigateur. Voir {{bug (870645)}}. Comme solution de rechange pour cette notification d'arrêt normal, vous pouvez suivre vos transactions et ajouter un événement  <code>beforeunload</code>  pour avertir l'utilisateur si des transactions ne sont pas encore terminées au moment du déchargement.</p>

<p>Au-moins, avec l'ajout des notifications d'annulation et {{domxref ("IDBDatabase.onclose")}}, vous pouvez savoir quand cela s'est produit.</p>

<h2 id="Le_tri_et_les_langues">Le tri et les langues</h2>

<p>Mozilla a implémenté la capacité d'effectuer un tri des données IndexedDB localisées sur Firefox 43+. Par défaut, IndexedDB n'a pas pris en charge l'internationalisation des chaînes de tri, et était trié comme s'il s'agissait d'un texte anglais. Par exemple, "b", "á", "z", "a" devaient être triés comme suit :</p>

<ul>
 <li>a</li>
 <li>b</li>
 <li>z</li>
 <li>á</li>
</ul>

<p>ce qui n'est évidemment pas la façon dont les utilisateurs souhaitent que leurs données soient triées - Aaron et Áaron, par exemple, doivent aller l'un à côté de l'autre dans une liste de contacts. L'obtention d'un tri international approprié exige donc que l'ensemble des données soit appelé dans la mémoire et que le tri soit exécuté par le JavaScript côté client, ce qui n'est pas très efficace.</p>

<p>Cette nouvelle fonctionnalité permet aux développeurs de spécifier une "locale" <em>(langue)</em> lors de la création d'un index en utilisant  {{domxref("IDBObjectStore.createIndex()")}}  (vérifiez ses paramètres). Dans ce cas, lorsqu'un curseur est utilisé pour itérer sur l'ensemble de données , et si vous souhaitez spécifier un tri local, vous pouvez utiliser un {{domxref ("IDBLocaleAwareKeyRange")}}.</p>

<p>{{domxref("IDBIndex")}} a également eu de nouvelles propriétés qui lui ont été ajoutées pour spécifier la langue : <code>locale</code> (retourne la langue si elle est spécifiée, ou null sinon) et <code>isAutoLocale</code> (retourne <code>true</code><em> (vrai)</em> si l'index a été créé avec une "locale auto", ce qui signifie que la langue par défaut de la plate-forme est utilisée, sinon <code>false</code>).</p>

<div class="note">
<p><strong>Note :</strong> Cette fonctionnalité est couramment cachée derrière une marque (flag) — pour l'activer et l'expérimenter, aller à about:config et activez <code>dom.indexedDB.experimental</code>.</p>
</div>

<h2 id="Full_IndexedDB_example">Exemple complet d'IndexedDB</h2>

<h3 id="HTML_Content">HTML Content</h3>

<pre class="brush: html">&lt;script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"&gt;&lt;/script&gt;

    &lt;h1&gt;IndexedDB Demo: storing blobs, e-publication example&lt;/h1&gt;
    &lt;div class="note"&gt;
      &lt;p&gt;
        Works and tested with:
      &lt;/p&gt;
      &lt;div id="compat"&gt;
      &lt;/div&gt;
    &lt;/div&gt;

    &lt;div id="msg"&gt;
    &lt;/div&gt;

    &lt;form id="register-form"&gt;
      &lt;table&gt;
        &lt;tbody&gt;
          &lt;tr&gt;
            &lt;td&gt;
              &lt;label for="pub-title" class="required"&gt;
                Title:
              &lt;/label&gt;
            &lt;/td&gt;
            &lt;td&gt;
              &lt;input type="text" id="pub-title" name="pub-title" /&gt;
            &lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
            &lt;td&gt;
              &lt;label for="pub-biblioid" class="required"&gt;
                Bibliographic ID:&lt;br/&gt;
                &lt;span class="note"&gt;(ISBN, ISSN, etc.)&lt;/span&gt;
              &lt;/label&gt;
            &lt;/td&gt;
            &lt;td&gt;
              &lt;input type="text" id="pub-biblioid" name="pub-biblioid"/&gt;
            &lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
            &lt;td&gt;
              &lt;label for="pub-year"&gt;
                Year:
              &lt;/label&gt;
            &lt;/td&gt;
            &lt;td&gt;
              &lt;input type="number" id="pub-year" name="pub-year" /&gt;
            &lt;/td&gt;
          &lt;/tr&gt;
        &lt;/tbody&gt;
        &lt;tbody&gt;
          &lt;tr&gt;
            &lt;td&gt;
              &lt;label for="pub-file"&gt;
                File image:
              &lt;/label&gt;
            &lt;/td&gt;
            &lt;td&gt;
              &lt;input type="file" id="pub-file"/&gt;
            &lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
            &lt;td&gt;
              &lt;label for="pub-file-url"&gt;
                Online-file image URL:&lt;br/&gt;
                &lt;span class="note"&gt;(same origin URL)&lt;/span&gt;
              &lt;/label&gt;
            &lt;/td&gt;
            &lt;td&gt;
              &lt;input type="text" id="pub-file-url" name="pub-file-url"/&gt;
            &lt;/td&gt;
          &lt;/tr&gt;
        &lt;/tbody&gt;
      &lt;/table&gt;

      &lt;div class="button-pane"&gt;
        &lt;input type="button" id="add-button" value="Add Publication" /&gt;
        &lt;input type="reset" id="register-form-reset"/&gt;
      &lt;/div&gt;
    &lt;/form&gt;

    &lt;form id="delete-form"&gt;
      &lt;table&gt;
        &lt;tbody&gt;
          &lt;tr&gt;
            &lt;td&gt;
              &lt;label for="pub-biblioid-to-delete"&gt;
                Bibliographic ID:&lt;br/&gt;
                &lt;span class="note"&gt;(ISBN, ISSN, etc.)&lt;/span&gt;
              &lt;/label&gt;
            &lt;/td&gt;
            &lt;td&gt;
              &lt;input type="text" id="pub-biblioid-to-delete"
                     name="pub-biblioid-to-delete" /&gt;
            &lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
            &lt;td&gt;
              &lt;label for="key-to-delete"&gt;
                Key:&lt;br/&gt;
                &lt;span class="note"&gt;(for example 1, 2, 3, etc.)&lt;/span&gt;
              &lt;/label&gt;
            &lt;/td&gt;
            &lt;td&gt;
              &lt;input type="text" id="key-to-delete"
                     name="key-to-delete" /&gt;
            &lt;/td&gt;
          &lt;/tr&gt;
        &lt;/tbody&gt;
      &lt;/table&gt;
      &lt;div class="button-pane"&gt;
        &lt;input type="button" id="delete-button" value="Delete Publication" /&gt;
        &lt;input type="button" id="clear-store-button"
               value="Clear the whole store" class="destructive" /&gt;
      &lt;/div&gt;
    &lt;/form&gt;

    &lt;form id="search-form"&gt;
      &lt;div class="button-pane"&gt;
        &lt;input type="button" id="search-list-button"
               value="List database content" /&gt;
      &lt;/div&gt;
    &lt;/form&gt;

    &lt;div&gt;
      &lt;div id="pub-msg"&gt;
      &lt;/div&gt;
      &lt;div id="pub-viewer"&gt;
      &lt;/div&gt;
      &lt;ul id="pub-list"&gt;
      &lt;/ul&gt;
    &lt;/div&gt;</pre>

<h3 id="CSS_Content">CSS Content</h3>

<pre class="brush: css">body {
  font-size: 0.8em;
  font-family: Sans-Serif;
}

form {
  background-color: #cccccc;
  border-radius: 0.3em;
  display: inline-block;
  margin-bottom: 0.5em;
  padding: 1em;
}

table {
  border-collapse: collapse;
}

input {
  padding: 0.3em;
  border-color: #cccccc;
  border-radius: 0.3em;
}

.required:after {
  content: "*";
  color: red;
}

.button-pane {
  margin-top: 1em;
}

#pub-viewer {
  float: right;
  width: 48%;
  height: 20em;
  border: solid #d092ff 0.1em;
}
#pub-viewer iframe {
  width: 100%;
  height: 100%;
}

#pub-list {
  width: 46%;
  background-color: #eeeeee;
  border-radius: 0.3em;
}
#pub-list li {
  padding-top: 0.5em;
  padding-bottom: 0.5em;
  padding-right: 0.5em;
}

#msg {
  margin-bottom: 1em;
}

.action-success {
  padding: 0.5em;
  color: #00d21e;
  background-color: #eeeeee;
  border-radius: 0.2em;
}

.action-failure {
  padding: 0.5em;
  color: #ff1408;
  background-color: #eeeeee;
  border-radius: 0.2em;
}

.note {
  font-size: smaller;
}

.destructive {
  background-color: orange;
}
.destructive:hover {
  background-color: #ff8000;
}
.destructive:active {
  background-color: red;
}</pre>

<h3 id="JavaScript_Content">JavaScript Content</h3>

<pre class="brush: js">(function () {
  var COMPAT_ENVS = [
    ['Firefox', "&gt;= 16.0"],
    ['Google Chrome',
     "&gt;= 24.0 (you may need to get Google Chrome Canary), NO Blob storage support"]
  ];
  var compat = $('#compat');
  compat.empty();
  compat.append('&lt;ul id="compat-list"&gt;&lt;/ul&gt;');
  COMPAT_ENVS.forEach(function(val, idx, array) {
    $('#compat-list').append('&lt;li&gt;' + val[0] + ': ' + val[1] + '&lt;/li&gt;');
  });

  const DB_NAME = 'mdn-demo-indexeddb-epublications';
  const DB_VERSION = 1; //  Utilisez un "long long" pour cette valeur (ne pas utiliser un flottant (float)) 
  const DB_STORE_NAME = 'publications';

  var db;

  //  Utilisé pour garder une trace de la vue affichée pour éviter de la recharger inutilement
  var current_view_pub_key;

  function openDb() {
    console.log("openDb ...");
    var req = indexedDB.open(DB_NAME, DB_VERSION);
    req.onsuccess = function (evt) {
      //  Le mieux utiliser "this" que "req" pour obtenir le résultat et éviter  
      // les problèmes avec "garbage collection".
      // db = req.result;
      db = this.result;
      console.log("openDb DONE");
    };
    req.onerror = function (evt) {
      console.error("openDb:", evt.target.errorCode);
    };

    req.onupgradeneeded = function (evt) {
      console.log("openDb.onupgradeneeded");
      var store = evt.currentTarget.result.createObjectStore(
        DB_STORE_NAME, { keyPath: 'id', autoIncrement: true });

      store.createIndex('biblioid', 'biblioid', { unique: true });
      store.createIndex('title', 'title', { unique: false });
      store.createIndex('year', 'year', { unique: false });
    };
  }

  /**
   * @paramètre {string}<em>(chaîne de caractères)</em> store_name
   * @paramètre {string}<em>(chaîne de caractères)</em> mode either "readonly" ou "readwrite"
   */
  function getObjectStore(store_name, mode) {
    var tx = db.transaction(store_name, mode);
    return tx.objectStore(store_name);
  }

  function clearObjectStore(store_name) {
    var store = getObjectStore(DB_STORE_NAME, 'readwrite');
    var req = store.clear();
    req.onsuccess = function(evt) {
      displayActionSuccess("Store cleared");
      displayPubList(store);
    };
    req.onerror = function (evt) {
      console.error("clearObjectStore:", evt.target.errorCode);
      displayActionFailure(this.error);
    };
  }

  function getBlob(key, store, success_callback) {
    var req = store.get(key);
    req.onsuccess = function(evt) {
      var value = evt.target.result;
      if (value)
        success_callback(value.blob);
    };
  }

  /**
   * @paramètre objet de stockage {IDBObjectStore=}
   */
  function displayPubList(store) {
    console.log("displayPubList");

    if (typeof store == 'undefined')
      store = getObjectStore(DB_STORE_NAME, 'readonly');

    var pub_msg = $('#pub-msg');
    pub_msg.empty();
    var pub_list = $('#pub-list');
    pub_list.empty();
    //  Réinitialisation de l'iframe afin qu'il n'indique pas le contenu précédent 
    newViewerFrame();

    var req;
    req = store.count();
    // Les requêtes sont exécutées dans l'ordre où elles ont été faites en-dehors de la 
    // transaction,  et leurs résultats sont retournés dans le même ordre. 
    // Ainsi, le texte du compteur ci-dessous sera affiché avant la liste de pub actuelle 
    // (ce n'est pas algorithmiquement important dans ce cas) .
    req.onsuccess = function(evt) {
      pub_msg.append('&lt;p&gt;There are &lt;strong&gt;' + evt.target.result +
                     '&lt;/strong&gt; record(s) in the object store.&lt;/p&gt;');
    };
    req.onerror = function(evt) {
      console.error("add error", this.error);
      displayActionFailure(this.error);
    };

    var i = 0;
    req = store.openCursor();
    req.onsuccess = function(evt) {
      var cursor = evt.target.result;

      //  Si le curseur pointe vers quelque chose, demandez les données 
      if (cursor) {
        console.log("displayPubList cursor:", cursor);
        req = store.get(cursor.key);
        req.onsuccess = function (evt) {
          var value = evt.target.result;
          var list_item = $('&lt;li&gt;' +
                            '[' + cursor.key + '] ' +
                            '(biblioid: ' + value.biblioid + ') ' +
                            value.title +
                            '&lt;/li&gt;');
          if (value.year != null)
            list_item.append(' - ' + value.year);

          if (value.hasOwnProperty('blob') &amp;&amp;
              typeof value.blob != 'undefined') {
            var link = $('&lt;a href="' + cursor.key + '"&gt;File&lt;/a&gt;');
            link.on('click', function() { return false; });
            link.on('mouseenter', function(evt) {
                      setInViewer(evt.target.getAttribute('href')); });
            list_item.append(' / ');
            list_item.append(link);
          } else {
            list_item.append(" / No attached file");
          }
          pub_list.append(list_item);
        };

        //  Passer à l'objet de stockage suivant
        cursor.continue();

        // Ce compteur sert seulement à créer des identifiants distincts
        i++;
      } else {
        console.log("No more entries");
      }
    };
  }

  function newViewerFrame() {
    var viewer = $('#pub-viewer');
    viewer.empty();
    var iframe = $('&lt;iframe /&gt;');
    viewer.append(iframe);
    return iframe;
  }

  function setInViewer(key) {
    console.log("setInViewer:", arguments);
    key = Number(key);
    if (key == current_view_pub_key)
      return;

    current_view_pub_key = key;

    var store = getObjectStore(DB_STORE_NAME, 'readonly');
    getBlob(key, store, function(blob) {
      console.log("setInViewer blob:", blob);
      var iframe = newViewerFrame();

      // Il n'est pas possible de définir un lien direct vers 
      // le blob pour fournir un moyen de le télécharger directement. 
      if (blob.type == 'text/html') {
        var reader = new FileReader();
        reader.onload = (function(evt) {
          var html = evt.target.result;
          iframe.load(function() {
            $(this).contents().find('html').html(html);
          });
        });
        reader.readAsText(blob);
      } else if (blob.type.indexOf('image/') == 0) {
        iframe.load(function() {
          var img_id = 'image-' + key;
          var img = $('&lt;img id="' + img_id + '"/&gt;');
          $(this).contents().find('body').html(img);
          var obj_url = window.URL.createObjectURL(blob);
          $(this).contents().find('#' + img_id).attr('src', obj_url);
          window.URL.revokeObjectURL(obj_url);
        });
      } else if (blob.type == 'application/pdf') {
        $('*').css('cursor', 'wait');
        var obj_url = window.URL.createObjectURL(blob);
        iframe.load(function() {
          $('*').css('cursor', 'auto');
        });
        iframe.attr('src', obj_url);
        window.URL.revokeObjectURL(obj_url);
      } else {
        iframe.load(function() {
          $(this).contents().find('body').html("No view available");
        });
      }

    });
  }

  /**
   * @paramètre {string} <em>(chaîne de caractères)</em> biblioid <em>(identifiant bibliothèque)</em>
   * @paramètre {string} <em>(chaîne de caractères) </em>title <em>(titre)</em>
   * @paramètre {number} <em>(nombre)</em> year <em>(année)</em>
   * @paramètre {string} <em>(chaîne de caractères) </em>url : l'URL de l'image à télécharger et stocker sur le pc
   *   IndexedDB database. La ressource derrière cette URL assujettie à
   *   "Same origin policy", donc pour que cette méthode fonctionne, l'URL doit venir de
   *   la même origine que le site web/l'application sur lequel le code est déployé.
   */
  function addPublicationFromUrl(biblioid, title, year, url) {
    console.log("addPublicationFromUrl:", arguments);

    var xhr = new XMLHttpRequest();
    xhr.open('GET', url, true);
    //  Définir le type de réponse recherché à "blob<code>"</code> 
    // http://www.w3.org/TR/XMLHttpRequest2/#the-response-attribute
    xhr.responseType = 'blob';
    xhr.onload = function (evt) {
                           if (xhr.status == 200) {
                             console.log("Blob retrieved");
                             var blob = xhr.response;
                             console.log("Blob:", blob);
                             addPublication(biblioid, title, year, blob);
                           } else {
                             console.error("addPublicationFromUrl error:",
                                           xhr.responseText, xhr.status);
                           }
                         };
    xhr.send();

    // Nous ne pouvons pas utiliser jQuery ici car, à partir de jQuery 1.8.3<code>,</code> 
    // le nouveau "blob" responseType n'est pas géré.
    // http://bugs.jquery.com/ticket/11461
    // http://bugs.jquery.com/ticket/7248
    // $.ajax({
    //   url: url,
    //   type: 'GET',
    //   xhrFields: { responseType: 'blob' },
    //   success: function(data, textStatus, jqXHR) {
    //     console.log("Blob retrieved");
    //     console.log("Blob:", data);
    //     // addPublication(biblioid, title, year, data);
    //   },
    //   error: function(jqXHR, textStatus, errorThrown) {
    //     console.error(errorThrown);
    //     displayActionFailure("Error during blob retrieval");
    //   }
    // });
  }

  /**
   * @paramètre {string} <em>(chaîne de caractères)</em> biblioid <em>(identifiant bibliothèque)</em>
   * @paramètre {string} <em>(chaîne de caractères) </em>title <em>(titre)</em>
   * @paramètre {number} <em>(nombre)</em> year <em>(année)</em>
   * @paramètre {Blob=} blob
   */
  function addPublication(biblioid, title, year, blob) {
    console.log("addPublication arguments:", arguments);
    var obj = { biblioid: biblioid, title: title, year: year };
    if (typeof blob != 'undefined')
      obj.blob = blob;

    var store = getObjectStore(DB_STORE_NAME, 'readwrite');
    var req;
    try {
      req = store.add(obj);
    } catch (e) {
      if (e.name == 'DataCloneError')
        displayActionFailure("This engine doesn't know how to clone a Blob, " +
                             "use Firefox");
      throw e;
    }
    req.onsuccess = function (evt) {
      console.log("Insertion in DB successful");
      displayActionSuccess();
      displayPubList(store);
    };
    req.onerror = function() {
      console.error("addPublication error", this.error);
      displayActionFailure(this.error);
    };
  }

  /**
   * @paramètre {string} <em>(chaîne de caractères)</em> biblioid <em>(identifiant bibliothèque)</em>
   */
  function deletePublicationFromBib(biblioid) {
    console.log("deletePublication:", arguments);
    var store = getObjectStore(DB_STORE_NAME, 'readwrite');
    var req = store.index('biblioid');
    req.get(biblioid).onsuccess = function(evt) {
      if (typeof evt.target.result == 'undefined') {
        displayActionFailure("No matching record found");
        return;
      }
      deletePublication(evt.target.result.id, store);
    };
    req.onerror = function (evt) {
      console.error("deletePublicationFromBib:", evt.target.errorCode);
    };
  }

  /**
   * @paramètre {number} <em>(nombre)</em> key <em>(clé)</em>
   * @paramètre {IDBObjectStore=} store <em>(objet de stockage)</em>
   */
  function deletePublication(key, store) {
    console.log("deletePublication:", arguments);

    if (typeof store == 'undefined')
      store = getObjectStore(DB_STORE_NAME, 'readwrite');

    // Selon les spécifications http://www.w3.org/TR/IndexedDB/#object-store-deletion-operation
    // le résultat de l'objet de stockage, l'algorithme de l'opération de suppression est
    // "undefined" (<em>indéfini</em>), donc il n'est pas possible de savoir si certains enregistrements
    // ont été effectivement supprimés en lisant le résultat de la requête.
    var req = store.get(key);
    req.onsuccess = function(evt) {
      var record = evt.target.result;
      console.log("record:", record);
      if (typeof record == 'undefined') {
        displayActionFailure("No matching record found");
        return;
      }
      // Attention:  La même clé utilisée pour la création doit être transmise pour 
      // la suppression.  Si la clé était un nombre pour la création, elle <code>doit</code>
      // être un nombre pour la suppression.
      req = store.delete(key);
      req.onsuccess = function(evt) {
        console.log("evt:", evt);
        console.log("evt.target:", evt.target);
        console.log("evt.target.result:", evt.target.result);
        console.log("delete successful");
        displayActionSuccess("Deletion successful");
        displayPubList(store);
      };
      req.onerror = function (evt) {
        console.error("deletePublication:", evt.target.errorCode);
      };
    };
    req.onerror = function (evt) {
      console.error("deletePublication:", evt.target.errorCode);
      };
  }

  function displayActionSuccess(msg) {
    msg = typeof msg != 'undefined' ? "Success: " + msg : "Success";
    $('#msg').html('&lt;span class="action-success"&gt;' + msg + '&lt;/span&gt;');
  }
  function displayActionFailure(msg) {
    msg = typeof msg != 'undefined' ? "Failure: " + msg : "Failure";
    $('#msg').html('&lt;span class="action-failure"&gt;' + msg + '&lt;/span&gt;');
  }
  function resetActionStatus() {
    console.log("resetActionStatus ...");
    $('#msg').empty();
    console.log("resetActionStatus DONE");
  }

  function addEventListeners() {
    console.log("addEventListeners");

    $('#register-form-reset').click(function(evt) {
      resetActionStatus();
    });

    $('#add-button').click(function(evt) {
      console.log("add ...");
      var title = $('#pub-title').val();
      var biblioid = $('#pub-biblioid').val();
      if (!title || !biblioid) {
        displayActionFailure("Required field(s) missing");
        return;
      }
      var year = $('#pub-year').val();
      if (year != '') {
        // Le mieux est d'utiliser Number.isInteger si le moteur a EcmaScript 6
        if (isNaN(year))  {
          displayActionFailure("Invalid year");
          return;
        }
        year = Number(year);
      } else {
        year = null;
      }

      var file_input = $('#pub-file');
      var selected_file = file_input.get(0).files[0];
      console.log("selected_file:", selected_file);
      // Garder une référence sur la façon de réinitialiser l'entrée du fichier dans l'interface
      // utilisateur une fois que nous<code> avons sa valeur</code>, mais au lieu de faire cela nous utiliserons
      // plutôt un type "reset" entré dans le formulaire HTML .
      // file_input.val(null);
      var file_url = $('#pub-file-url').val();
      if (selected_file) {
        addPublication(biblioid, title, year, selected_file);
      } else if (file_url) {
        addPublicationFromUrl(biblioid, title, year, file_url);
      } else {
        addPublication(biblioid, title, year);
      }

    });

    $('#delete-button').click(function(evt) {
      console.log("delete ...");
      var biblioid = $('#pub-biblioid-to-delete').val();
      var key = $('#key-to-delete').val();

      if (biblioid != '') {
        deletePublicationFromBib(biblioid);
      } else if (key != '') {
        // Le mieux est d'utiliser Number.isInteger si le moteur a EcmaScript 6
        if (key == '' || isNaN(key))  {
          displayActionFailure("Invalid key");
          return;
        }
        key = Number(key);
        deletePublication(key);
      }
    });

    $('#clear-store-button').click(function(evt) {
      clearObjectStore();
    });

    var search_button = $('#search-list-button');
    search_button.click(function(evt) {
      displayPubList();
    });

  }

  openDb();
  addEventListeners();

})(); // Immediately-Invoked Function Expression (IIFE)</pre>

<p>{{ LiveSampleLink('Full_IndexedDB_example', "Test the online live demo") }}</p>

<h2 id="Voir_aussi">Voir aussi</h2>

<p>Référence :</p>

<ul>
 <li><a href="/en/IndexedDB">IndexedDB API Reference</a></li>
 <li><a href="http://www.w3.org/TR/IndexedDB/">Indexed Database API Specification</a></li>
 <li><a href="/en-US/docs/IndexedDB/Using_IndexedDB_in_chrome">Using IndexedDB in chrome</a></li>
 <li><a href="/en-US/docs/Web/API/IndexedDB_API/Using_JavaScript_Generators_in_Firefox">Using JavaScript generators in Firefox</a></li>
 <li>IndexedDB <a href="https://mxr.mozilla.org/mozilla-central/find?text=&amp;string=dom%2FindexedDB%2F.*%5C.idl&amp;regexp=1">interface files</a> dans le code source de Firefox</li>
</ul>

<p>Tutoriels :</p>

<ul>
 <li><a href="http://www.html5rocks.com/en/tutorials/indexeddb/uidatabinding/">Databinding UI Elements with IndexedDB</a></li>
 <li><a href="http://msdn.microsoft.com/en-us/scriptjunkie/gg679063.aspx">IndexedDB — The Store in Your Browser</a></li>
</ul>

<p>Bibliothèques :</p>

<ul>
 <li><a href="http://mozilla.github.io/localForage/">localForage </a>: Un Polyfill qui fournit un nom simple : la syntaxe de valeur pour le stockage de données côté client, qui utilise IndexedDB en arrière-plan, mais retourne à WebSQL puis à localStorage pour les navigateurs qui ne prennent pas en charge IndexedDB.</li>
 <li><a href="http://www.dexie.org/">dexie.js </a>: Une enveloppe pour IndexedDB qui permet un développement de code beaucoup plus rapide grâce à une syntaxe simple et agréable.</li>
 <li><a href="https://github.com/erikolson186/zangodb">ZangoDB </a>: Un MongoDB-like interface pour IndexedDB qui prend en charge la plupart des fonctionnalités familières de filtrage, projection, tri, mise à jour et agrégation de  MongoDB.</li>
 <li><a href="http://jsstore.net/">JsStore</a> : Une enveloppe d'IndexedDB simple et avancée ayant une syntaxe SQL.</li>
</ul>