ตัว web เป็น package ที่รวบรวม Web API ต่าง ๆ ที่ทำงานผ่าน JavaScript โดยตัว webdev ถูกสร้างโดยอ้างอิงจาก Web IDL และไม่มี overhead ในการเชื่อมต่อระหว่าง Dart และ JavaScript ดังนั้นประโยชน์หลัก ๆ ในการใช้งาน web คือ การเรียก Web API เพื่อสื่อสารกับเว็บเบราเซอร์ เลยจะมาบันทึก API ที่น่าจะใช้บ่อย ๆ
ค่า URL ที่ใช้เรียกตัวเว็บ จะสามารถใช้ Uri class เพื่อประมวลผลแยกส่วนต่าง ๆ ออกมาได้
import 'package:web/web.dart' as web;
void main() {
Uri url = Uri.parse(web.document.URL);
url.queryParameters.forEach((key, value) => print("$key: $value"));
}
ลองใส่ ?hello=test&demo=1
วิธีการตรวจสอบ referrer ว่าลิงก์มาจากไหน สามารถใช้คำสั่ง document.referrer
คืนมาเป็น String ของ URI ที่เป็นตัวลิงก์มาได้
import 'package:web/web.dart' as web;
void main() {
Uri? referrer = Uri.tryParse(web.document.referrer);
if (referrer == null) {
print('invalid referrer');
} else {
print(referrer);
}
}
Title ของเว็บเพจ สามารถอ่านและแก้ไขค่าได้ด้วยคำสั่ง document.title
import 'package:web/web.dart' as web;
void main() {
String pageTitle;
pageTitle = web.document.title;
print('old title is "$pageTitle"');
pageTitle = 'happy web api';
web.document.title = pageTitle;
print('new title is "$pageTitle"');
}
ลองเปลี่ยน Title ของ webpage
เป็นคำสั่งที่ช่วยในการเลือก element ที่ตรงกับที่ระบุ ผ่าน CSS selectors
.querySelector()
ตัวอย่าง หากต้องการเลือก <div id="output"></div>
เพื่อแก้ไขข้อความ สามารถเขียนคำสั่ง document.querySelector()
ดังนี้
import 'package:web/web.dart' as web;
void main() {
web.Element? div1 = web.document.querySelector("div#output");
div1?.textContent = "this is match element";
}
ผลการแก้ไข div
.querySelectorAll()
การทำงานจะเหมือนกับ document.querySelector()
แต่
document.querySelectorAll()
จะคืนค่ามาเป็น NodeList
ตัวอย่าง หากต้องการเลือก <div>
ทุกอันใน document สามารถทำได้ดังนี้
import 'package:web/web.dart' as web;
void main() {
web.NodeList divs = web.document.querySelectorAll("div");
print("found div element = ${divs.length}");
for (var i = 0; i < divs.length; i++) {
divs.item(i)?.textContent = "This is div number $i";
}
}
มี <div> จำนวน 3 โหนด
ตัว CSS selectors มีหลาย levels จากที่หาข้อมูล level 3 รองรับที่เว็บเบราเซอร์ในแล้ว วิธีการเขียนมีสอนเขียน CSS selectors เลือกที่น่าสนใจและใช้งานบ่อย ๆ
h1
h2
p
div
.
นำหน้าชื่อ class เช่น
<xxx class="demo1>
ถ้าไม่สนใจ tag ตัวเลือกคือ .demo1
<div class="demo1>
ถ้าสนใจว่าเป็น div เท่านั้น ตัวเลือกคือ div.demo1
<xxx class="a b c">
ถ้าต้องการ tag ที่มีหลาย class ตัวเลือกคือ .a.b.c
สามารถสลับตำแหน่งได้#
<xxx id="xx">
ถ้าไม่สนใจ tag ตัวเลือกคือ #xx
<div id="xx">
ถ้าสนใจว่าเป็น div เท่านั้น ตัวเลือกคือ div#xx
,
เพื่อเลือกเกิน 1 เงื่อนไข เช่น h1,h2,h3
จะตรงกับ html tag → h1 h2 h3 ที่อยู่ใน document>
เพื่อเลือกตัวลูกที่อยู่ในลำดับถัดไป เช่น <div><p>1</p><div><p>2</p></div></div>
ถ้าเขียนตัวเลือก div > p
จะเลือก <p>1</p>
เพื่อเลือกตัวลูกที่อยู่ข้างใน เช่น <div id="x"><div><p>1</p></div></div><p>2</p>
ถ้าเขียนตัวเลือก div#x p
จะเลือก <p>1</p>
ที่อยู่ใน div เท่านั้น โดยไม่สนใจลำดับความลึก+
เพื่อเลือกตัวที่อยู่ระดับเดียวกันและอยู่ติดกัน เช่น <p class="a"></p><p class="b"></p><p class="c"></p>
.a + .b
จะเลือก <p class="b"></p>
.a + .c
จะไม่เลือก ❌ เพราะ <p class="c"></p>
ไม่ได้อยู่ติดกับ <p class="a"></p>
~
เพื่อเลือกตัวที่อยู่ระดับเดียวกันและอยู่ถัดไป เช่น <p class="a"></p><p class="b"></p><p class="c"></p>
ถ้าเขียน .a ~ .c
จะเลือก <p class="c"></p>
ได้ เพราะอยู่ในระดับเดียวกันแม้จะได้อยู่ติดกันก็ตาม:not()
แทนนิเสธ เช่น <p class="a"></p><p class="b"></p><p></p>
ถ้าเขียน :not(.a)
จะได้ <p class="b"></p>
และ <p></p>
:empty
เลือก tag ที่ไม่มีข้อมูลข้างใน เช่น <p></p><p>a</p><div></div>
:empty
จะได้ <p></p>
และ <div></div>
p:empty
จะได้ <p></p>
หากต้องการทดสอบเขียน CSS ที่ต้องการ สามารถ developer tool ของ Chrome ได้
ลองเพิ่ม CSS เพื่อเปลี่ยนสีของ div ตัวแรกดู
cookie เป็นข้อมูลขนาดเล็กใช้สำหรับเก็บข้อมูลระหว่างใช้งานบนเว็บนั้น ๆ โดยจะมีการส่ง cookie ออกไปที่เซิร์ฟเวอร์ทุกครั้งที่มีการสร้าง request การเข้าถึง cookie ใช้คำสั่ง document.cookie
import 'package:web/web.dart' as web;
void main() {
// write cookie
// format: key=value;expires=[date];path=[/path/to/store/cookie];
web.document.cookie = 'firstname=John;expires=Wed, 18 Dec 2026 12:00:00 GMT';
web.document.cookie = 'lastname=Smith;expires=Wed, 18 Dec 2026 12:00:00 GMT';
// get all cookie
var allCookie = web.document.cookie;
print("current cookie: $allCookie"); // output → current cookie: firstname=John; lastname=Smith
// delete cookie for firstname
web.document.cookie = 'firstname=;expires=Wed, 01 Jan 1970 12:00:00 GMT';
// display remain cookie
allCookie = web.document.cookie;
print("current cookie: $allCookie"); // output → current cookie: lastname=Smith
}
คำสั่ง window.localStorage
จะใช้สำหรับเข้าถึง Storage
object ที่ใช้สำหรับเก็บข้อมูลในฝั่ง client โดยจะเก็บลงในเว็บเบราเซอร์แบบ key-value pair ข้อมูลที่เก็บจะรับเฉพาะ String และเข้ารหัสแบบ UTF-8
import 'package:web/web.dart' as web;
void main() {
web.Storage storage = web.window.localStorage;
print("Storage length: ${storage.length}"); // output → Storage length: 0
// add data
storage.setItem('font-size', '14');
storage.setItem('theme', 'dark');
print("Storage length: ${storage.length}"); // output → Storage length: 2
// read data
String? selectTheme = storage.getItem('theme');
print("Current theme: $selectTheme"); // output → Current theme: dark
// clear all data
storage.clear();
print("Storage length: ${storage.length}"); // output → Storage length: 0
}
ในกรณีที่ต้องการจะเก็บข้อมูลแค่ชั่วคราว เมื่อปิดเบราเซอร์แล้วให้ลบทิ้ง ให้บันทึกลง window.sessionStorage
แทน
ข้อมูลที่เก็บใน localStorage และ sessionStorage ไม่ควรเป็นข้อมูลที่มีความอ่อนไหว เช่น password, encrypt key, session token เนื่องจากสามารถใช้ Developer tools เขียนคำสั่งอ่านข้อมูลได้ตลอดเวลา และการใช้งานต้องตรวจสอบความถูกต้องก่อนทุกครั้ง ให้คำนึงเสมอว่าเป็นข้อมูลที่อาจถูกแทรกแซงได้เสมอ และขนาดพื้นที่สูงสุดที่สามารถเก็บข้อมูลทั้งหมดได้ประมาณ 5MB เท่านั้น
หากต้องการเก็บที่เป็นข้อมูลอย่างอื่นที่ไม่ใช่แค่ String หรือมีขนาดใหญ่เกินกว่า 5 MB IndexedDB เป็นขั้นกว่าของ localStorage การทำงานจะเหมือนพวกฐานข้อมูลแบบ NoSQL มันสามารถใส่ Object ที่ต้องการได้เลย การเข้าถึง API สามารถเรียกผ่านคำสั่ง window.indexedDB
หากเบราเซอร์รองรับจะได้ IDBFactory
กลับมา
สอนวิธีการใช้ IndexedDB: Work with IndexedDB จาก web.dev
เนื่องจากตัว มีคำแนะนำ migration ในการย้ายไปใช้ web package ค่อนข้างจำกัด และไม่มีตัวอย่างการใช้งานกับ IndexedDB โดยตรง ณ ปัจจุบัน การใช้ dart:indexed_db น่าจะเป็นทางออกที่ดีกว่า รอจนกว่าเอกสารและตัวอย่างมากกว่านี้ค่อย migration ไป web package
ตัว Cache ช่วยให้เว็บแอปสามารถดึงข้อมูลมาเก็บไว้ที่เบราเซอร์แบบ offline ได้ เบราเซอร์ตั้งแต่ปี ค.ศ.2017 เป็นต้นมารองรับหมดแล้ว
ตัวอย่าง ไฟล์ offline.txt เป็นข้อมูลที่จะดึงมาเก็บไว้ใน Cache ชื่อ offline-cache
ในการใช้งานในชีวิตจริง จะใช้กับคำสั่ง window.fetch()
หากเกิดปัญหาไม่สามารถติดต่อกับเซิร์ฟเวอร์ได้ ก็จะได้ไปเลือกข้อมูลใน Cache ที่เก็บไว้มาใช้งานแทนเป็นต้น
import 'dart:js_interop';
import 'package:web/web.dart' as web;
void main() async {
web.Cache cache = await web.window.caches.open('offline-cache').toDart;
cache.add(web.Request('/offline.txt'.toJS)).toDart.then((_) {
print('offline.txt cached');
}, onError: (e) {
print('Error $e');
});
}
เขียนคำสั่งอ่าน offline.txt มาไว้ใน Cache