Lecture 3 — Client-side Programming: Web Storage, jQuery, JSON, AJAX
1. Web Storage
1.1 What is Web Storage?
浏览器在client-side保存数据,使网页可以读取此前保存的preferences或state
browser storage mechanisms包含cookies、sessionStorage、localStorage
浏览器存储的数据不会因为网页刷新就必然消失,具体lifetime取决于storage type
1.2 Benefits and Advantages
Persistence:localStorage可在关闭浏览器后保留数据;不能把这一特点直接套用于sessionStorage
Capacity:sessionStorage/localStorage通常比单个cookie能保存更多数据
Efficient handling:通过简单key-value API进行读取、更新和删除
Local access:读写不需要每次请求server,可减少网络访问并保存临时state
1.3 Difference with DB Services
对比项 Browser Storage Server-side Database 数据位置 当前browser/device 服务器或数据库服务 组织方式 通常用于少量key-value数据 可处理复杂查询、关系和更大数据集 读写途径 页面JavaScript或cookie机制 通过后端/API访问 跨设备共享 不会自动跨设备同步 可由服务端集中管理 典型用途 preferences、缓存、temporary state 用户记录、交易、共享业务数据
Client-side storage不替代server-side database;本地值可以被用户修改,不能单凭它判断服务端身份或权限
1.4 Cookies
What are Cookies?
Cookie是网站让浏览器保存的少量name-value data,常用于preferences、session management和tracking
与Web Storage不同,符合domain/path等条件的cookies通常会随HTTP requests自动发送
Server可通过Set-Cookie设置cookie;JavaScript可通过document.cookie读写非HttpOnly cookies
Benefits of Cookies
保存language/theme等user-specific settings,提供personalized experience
保存session相关标识,让server关联多次requests中的用户状态
用于tracking与analytics;自动随匹配requests发送,便于server读取
Types of Cookies
Session Cookies
不设置Expires/Max-Age时通常是session cookie,在browser session结束时到期
Persistent Cookies
设置expiry后,在指定lifetime内保留,可跨browser sessions使用
到期、用户清除cookie或浏览器策略处理后仍会删除
First-party Cookies
在用户当前访问网站的first-party context中使用的cookies
Third-party Cookies
在其他网站提供的third-party context中使用的cookies,常见于嵌入内容、广告和tracking
Lifecycle of Cookies
Create
给document.cookie赋值设置一个cookie,可附加expires、max-age和path
下例保存普通username显示值,Max-Age=3600表示3600秒,Path=/覆盖该站点路径
document.cookie = "username=John; Max-Age=3600; Path=/" ;
Read
document.cookie返回当前页面可访问的cookie name-value pairs,通常以; 分隔
不会把Expires、Path等attributes作为普通cookie values返回
console. log (document.cookie); // 例如:username=John; theme=dark
Update
使用相同name及对应path/domain重新设置value,并非直接替换整个cookie集合
document.cookie = "username=Jane; Max-Age=3600; Path=/" ;
Delete
设置已到期的expiry或Max-Age=0
仅把value设为空,不等于删除
删除时要匹配原cookie的name、path及适用的domain
document.cookie = "username=; Max-Age=0; Path=/" ;
// 课件的另一种写法:
document.cookie = "username=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/" ;
1.5 Session Storage
What is Session Storage?
sessionStorage保存当前page session的数据,以origin与top-level tab/window区分storage area
刷新页面通常保留数据;关闭对应tab/window、结束page session后通常清除
sessionStorage 只留在本标签页、而cookies会发往服务器
Store Data
setItem(key, value)存入data
key/value以strings保存,非string value会进行string conversion
sessionStorage. setItem ( "username" , "John Doe" );
sessionStorage. setItem ( "step" , 2 ); // 实际保存字符串"2"
Retrieve Data
getItem(key)返回对应string,key不存在时返回null
const name = sessionStorage. getItem ( "username" );
const step = Number (sessionStorage. getItem ( "step" ));
console. log (name, step); // John Doe 2
Update Data
再次用同一个key调用setItem(),覆盖原value
sessionStorage. setItem ( "username" , "Jane Doe" );
Remove Data
removeItem(key)只删除指定entry
clear()删除该storage area内的所有entries
sessionStorage. removeItem ( "username" );
console. log (sessionStorage. getItem ( "username" )); // null
在浏览器DevTools的Application/Storage面板中找到Session Storage,再选择当前origin
可以查看保存的key-value pairs,确认set/update/remove后的变化
1.6 Local Storage
What is Local Storage?
localStorage按origin隔离,同一origin下的不同tabs通常访问同一个local storage area
没有内置expiration date,数据可以跨browser sessions保留
Store Data
与sessionStorage使用同一组Storage methods:setItem/getItem/removeItem/clear
localStorage. setItem ( "theme" , "dark" );
Retrieve & Update Data
console. log (localStorage. getItem ( "theme" )); // dark(此前已保存时)
localStorage. setItem ( "theme" , "light" );
Remove Data
删除指定key;DevTools中检查Local Storage,而不是Session Storage
localStorage. removeItem ( "theme" );
Store an Object
直接存object会得到类似"[object Object]"的string;保存结构化数据通常先JSON.stringify(),读取后JSON.parse()
const preferences = { theme: "dark" , fontSize: 18 };
localStorage. setItem ( "preferences" , JSON . stringify (preferences));
const saved = localStorage. getItem ( "preferences" );
const restored = saved === null ? null : JSON . parse (saved);
console. log (restored.theme); // dark
1.7 Cookies vs Session Storage vs Local Storage
对比项 Cookies sessionStorage localStorage API document.cookie、HTTP headersStorage API Storage API 主要scope domain/path及相关cookie规则 origin + tab/page session origin lifetime session或指定expiry page session 没有内置expiry 随HTTP自动发送 匹配条件时会 不会 不会 跨tabs 符合cookie scope时可访问 各自独立,可能复制opener初值 同origin通常共用 典型容量 单个cookie约4KB 课件给出约5–10MB 课件给出约5–10MB 适合用途 session标识、少量preferences 一次流程的temporary state 跨sessions preferences与缓存
2. jQuery
2.1 What is jQuery?
jQuery是JavaScript library,简化DOM traversal/manipulation、event handling、effects与AJAX
使用$()或jQuery()得到jQuery object,再调用methods
2.2 Why use jQuery?
使用CSS-like selectors定位elements,DOM操作较简短
提供event、animation与AJAX APIs,减少重复代码
处理部分cross-browser differences,并有plugin ecosystem
2.3 Setup & Download Options
Local File
从official website下载JS文件,再通过external script引入
Minified/compressed版本适合减少传输量,uncompressed版本方便阅读和debugging
< script src = "js/jquery-4.0.0.min.js" ></ script >
CDN
< script src = "https://code.jquery.com/jquery-4.0.0.min.js" ></ script >
2.4 Hello World Syntax
Selectors
基本形式:$(selector).method(arguments)
$("#id")按ID选择,$(".class")按class选择,$("p")按tag选择
返回jQuery wrapper;与document.getElementById()返回DOM element不同
$ ( "#myElement" ). text ( "Hello, World!" );
Document Ready
$(function () { ... })在DOM可用后执行,不需要等待所有images下载完成
将script放在目标elements之后也可避免过早访问DOM
<! DOCTYPE html >
< html lang = "en" >
< head >
< meta charset = "UTF-8" >
< title >jQuery Hello World</ title >
< script src = "https://code.jquery.com/jquery-4.0.0.min.js" ></ script >
</ head >
< body >
< div id = "myElement" ></ div >
< script >
$ ( function () { $ ( "#myElement" ). text ( "Hello, World!" ); });
</ script >
</ body >
</ html >
2.5 Essential Attributes & Properties
html()
不带argument读取inner HTML;带argument替换内部HTML,tags会被解析
对多个matched elements,getter通常取得第一个element,setter作用于全部matched elements
const content = $ ( "#output" ). html ();
$ ( "#output" ). html ( "<strong>Hello</strong>" );
text()
读写text content,写入的tags会作为literal text显示
显示user-entered text时使用text可避免将其当作HTML解析
$ ( "#output" ). text ( "<strong>Hello</strong>" ); // 显示字面文本
const content = $ ( "#output" ). text ();
val()
读写input、select和textarea等form controls的value
程序调用.val(...)改变value,不会自动触发change event
$ ( "#nameInput" ). val ( "John Doe" );
const name = $ ( "#nameInput" ). val ();
css()
.css("property")读取CSS property,.css("property", value)设置property
可以传入object,一次设置多个properties;明确写units可减少版本差异
const color = $ ( "#output" ). css ( "color" );
$ ( "#output" ). css ({ "background-color" : "blue" , "font-size" : "18px" });
2.6 Insert and Remove Content
append()
在selected element内部末尾加入content,不替换已有内容
$ ( "#myList" ). append ( "<li>Last item</li>" );
prepend()
在selected element内部开头加入content
$ ( "#myList" ). prepend ( "<li>First item</li>" );
after()
在selected element外部紧接其后插入sibling
$ ( "#myDiv" ). after ( "<p>After the div</p>" );
before()
在selected element外部紧接其前插入sibling
$ ( "#myDiv" ). before ( "<p>Before the div</p>" );
remove()
删除selected element本身及内部内容,同时清理其jQuery-managed data/event handlers
$ ( "#myDiv" ). remove ();
empty()
清除selected element的children与content,保留element本身
$ ( "#myDiv" ). empty (); // div仍存在,内部变空
方法 插入/删除的位置 目标element是否保留 append / prepend 内部末尾 / 开头 是 after / before 外部后方 / 前方 是 remove 目标与其内部内容 否 empty 仅内部内容 是
2.7 CSS Class Manipulation
addClass()
添加一个或多个classes;实际视觉效果来自对应CSS rule
$ ( "#myDiv" ). addClass ( "highlight" );
removeClass()
$ ( "#myDiv" ). removeClass ( "highlight" );
toggleClass()
指定class存在就移除,不存在就添加
使用明确的class name,避免依赖不同版本中已变更的旧Boolean-only signature
< style > .highlight { background-color : yellow ; }</ style >
< div id = "myDiv" >This is a div.</ div >
< button id = "toggleClassButton" type = "button" >Toggle Class</ button >
< script >
$ ( "#toggleClassButton" ). on ( "click" , function () {
$ ( "#myDiv" ). toggleClass ( "highlight" );
});
</ script >
2.8 Visibility and Effects
hide() and show()
hide()使element隐藏,不保留其layout space;element仍存在于DOM
show()使隐藏element恢复显示,不代表一律强制设置display: block
$ ( "#myDiv" ). hide ();
$ ( "#myDiv" ). show ();
fadeIn() and fadeOut()
通过opacity产生渐显/渐隐效果,duration以milliseconds指定
fadeOut()结束后通常隐藏element;与remove()删除DOM element不同
$ ( "#myDiv" ). fadeOut ( 1000 ); // 1秒渐隐
$ ( "#myDiv" ). fadeIn ( 1000 ); // 1秒渐显
slideUp() and slideDown()
通过height等相关属性的变化产生收起/展开效果,不是简单向上/向下移动position
$ ( "#myDiv" ). slideUp ( 500 );
$ ( "#myDiv" ). slideDown ( 500 );
2.9 Event Handling
click() and on()
.on("click", handler)本身是direct binding,只绑定当前selected elements
// Direct binding:绑定当前已经存在的button
$ ( "#myButton" ). on ( "click" , function () { console. log ( "Clicked" ); });
// Delegation:#myList需已经存在;以后新增的li也可被处理
$ ( "#myList" ). on ( "click" , "li" , function () {
console. log ( $ ( this ). text ());
});
hover()
使用.hover(handlerIn, handlerOut)处理mouseenter与mouseleave
$ ( "#myDiv" )
. on ( "mouseenter" , function () { $ ( this ). text ( "Mouse entered!" ); })
. on ( "mouseleave" , function () { $ ( this ). text ( "Mouse left!" ); });
focus()
Focus表示element获得输入焦点
可用.on("focus", handler)监听,用.trigger("focus")主动触发
$ ( "#focusButton" ). on ( "click" , function () {
$ ( "#myInput" ). trigger ( "focus" );
});
blur()
Element失去focus时触发,可用于用户离开input后的处理
不需要value一定变化
$ ( "#nameInput" ). on ( "blur" , function () {
console. log ( $ ( this ). val ());
});
keydown()
$ ( "#myInput" ). on ( "keydown" , function ( event ) {
$ ( "#keyInfo" ). text ( "Key pressed: " + event.key);
});
keyup()
$ ( "#myInput" ). on ( "keyup" , function ( event ) {
$ ( "#keyInfo" ). text ( "Key released: " + event.key);
});
change()
Text input通常在value发生变化并失去focus后触发;仅失去focus但未修改value不一定触发
Select、checkbox等controls的change时机不同,不能一概理解为都要先失去focus
$ ( "#myInput" ). on ( "change" , function () {
$ ( "#valueDisplay" ). text ( "Value changed: " + $ ( this ). val ());
});
this and $(this)
Traditional function event handler中的this是当前处理event的DOM element
delegated handler中是匹配selector的element
$(this)将该DOM element包装成jQuery object,才能调用.val()、.text()等methods
3. JSON
3.1 What is JSON?
JSON(JavaScript Object Notation)是lightweight data interchange format,用于存储和传输structured data
常用于API responses、configuration,以及不同系统之间的数据交换
3.2 Basic Syntax
Objects and Key-value Pairs
JSON object用{}包围,key必须是double-quoted string,key/value之间用:分隔
多个properties之间用,分隔,不允许trailing comma
Arrays and Values
Array用[]包围;string同样使用double quotes;Boolean和null不加引号
JSON不支持comments、functions、undefined、NaN或Infinity
JSON顶层也可以是array或primitive value,不必始终是object
{
"name" : "John Doe" ,
"age" : 30 ,
"isStudent" : false ,
"hobbies" : [ "reading" , "coding" , "gaming" ],
"address" : { "street" : "123 Main St" , "city" : "New York" },
"isActive" : null
}
3.3 JavaScript Object vs JSON
对比项 JavaScript Object JSON text 性质 程序运行时的value 用于交换或存储的text format property name 可使用部分不加引号的names 必须使用double quotes 内容 可以包含functions等JS values 仅支持JSON规定的types 操作 访问properties、赋值、delete 通常先parse成JS value再操作
const person = { name: "John" , age: 30 }; // JavaScript object
const jsonText = '{"name":"John","age":30}' ; // string containing JSON
console. log ( typeof person); // object
console. log ( typeof jsonText); // string
3.4 JSON Conversion
JSON.stringify()
Serialization:把可序列化的JavaScript value转换成JSON string
JSON.stringify(value, null, 2)用2个spaces缩进,适合阅读输出
const person = { name: "John Doe" , age: 30 };
const jsonString = JSON . stringify (person);
console. log (jsonString); // {"name":"John Doe","age":30}
JSON.parse()
Deserialization:将有效JSON text转换成对应JavaScript value;无效JSON会throw SyntaxError
const jsonString = '{"name":"John Doe","age":30}' ;
const person = JSON . parse (jsonString);
console. log (person.name); // John Doe
console. log ( JSON . parse ( "true" )); // true
Display Parsed Data
把一般object插入text output,得到[object Object]
展示JSON text时使用stringify;展示一个field时取对应property
const person = { name: "John Doe" , age: 30 };
$ ( "#output" ). text ( JSON . stringify (person, null , 2 ));
// 或:$("#output").text(person.name);
3.5 Add Item to JSON Data
修改JSON实际是在修改parse后的JavaScript object
用dot notation或bracket notation新增property,之后需要JSON text时再stringify
const person = JSON . parse ( '{"name":"John Doe","age":30}' );
person.newKey = "New Value" ;
person[ "country" ] = "USA" ;
console. log (person.newKey); // New Value
3.6 Update Item from JSON Data
const person = { name: "John Doe" , age: 30 };
person.age = 31 ;
console. log (person.age); // 31
3.7 Remove Item from JSON Data
delete object.key删除property;与将value设为null不同,后者仍保留key
const person = { name: "John Doe" , isStudent: false };
delete person.isStudent;
console. log ( "isStudent" in person); // false
3.8 JSON Array
有序values用[]表示,可混合JSON允许的不同types;index从0开始
[ 1 , "two" , true , { "name" : "John Doe" }]
const values = JSON . parse ( '[1,"two",true,{"name":"John Doe"}]' );
console. log (values[ 1 ]); // two
console. log (values[ 3 ].name); // John Doe
4. AJAX
4.1 What is AJAX?
AJAX = Asynchronous JavaScript and XML,是浏览器进行asynchronous HTTP requests的技术
收到response后,只更新需要变化的页面部分,不必reload整个页面
名称包含XML,但可交换JSON、text等格式;AJAX不是专门的data format或独立programming language
Asynchronous表示等待response期间可以继续执行后续代码,不表示请求结果已经即时可用
4.2 Traditional AJAX
XMLHttpRequest
new XMLHttpRequest()建立request object
open(method, url, true)配置asynchronous request;send()发出request
onreadystatechange在state变化时执行;readyState === 4表示完成,再检查HTTP status
function getWeather () {
const xhr = new XMLHttpRequest ();
xhr. open ( "GET" , "/weather" , true ); // 示例:需要对应server endpoint
xhr. onreadystatechange = function () {
if (xhr.readyState !== 4 ) { return ; }
if (xhr.status >= 200 && xhr.status < 300 ) {
document. getElementById ( "output" ).textContent = xhr.responseText;
} else {
document. getElementById ( "output" ).textContent = "Request failed: " + xhr.status;
}
};
xhr. send ();
}
4.3 AJAX Basics with jQuery
$.ajax()
通用request API,可设置url、method、data、dataType等options,返回jqXHR
dataType: "json"说明期望response格式 ,jQuery解析有效JSON后把JavaScript value传给callback
.done()处理success,.fail()处理failure;必须等callback执行时才能使用response
$.get() and $.post()
GET/POST的shorthand,适合较简单的requests;返回的jqXHR也可接.done()和.fail()
$. get ( "/data.json" )
. done ( function ( data ) { console. log (data); })
. fail ( function () { console. log ( "Request failed" ); });
// /data.json需由自己的server提供
Callbacks and Options
更正:课件pp.81–82将done、fail写在$.ajax({...})options内部,不能作为标准success/error options使用
应写$.ajax({...}).done(...).fail(...),或在options中使用success与error。官方API
jqXHR.success()旧method与success:option不同,不要混淆
4.4 AJAX GET Request with jQuery
Cat Fact Example
课件p.81:点击button,GET https://catfact.ninja/fact,将JSON response显示到页面
以下改为正确的callback chaining,使用text避免把response当作HTML解析
< button id = "fetchDataBtn" type = "button" >Fetch Data</ button >
< pre id = "output" ></ pre >
< script >
$ ( function () {
$ ( "#fetchDataBtn" ). on ( "click" , function () {
$ ( "#output" ). text ( "Fetching..." );
$. ajax ({
url: "https://catfact.ninja/fact" ,
method: "GET" ,
dataType: "json"
})
. done ( function ( response ) {
$ ( "#output" ). text ( JSON . stringify (response, null , 2 ));
})
. fail ( function ( xhr , textStatus ) {
$ ( "#output" ). text ( "Error fetching data: " + textStatus);
});
});
});
</ script >
上例需先加载jQuery完整build;API response内容取决于运行时返回值,不是固定例句
补充:指定dataType: "json"后,response通常已被解析,不需要再次对它调用JSON.parse()
Hong Kong Observatory Example
课件p.82使用HKO Open Data的weather forecast endpoint,同样通过GET取得JSON
与Cat Fact例子的主要区别是URL和response fields;页面只显示需要的field时可以进一步取properties
$. ajax ({
url: "https://data.weather.gov.hk/weatherAPI/opendata/weather.php" ,
method: "GET" ,
data: { dataType: "fnd" , lang: "en" },
dataType: "json"
})
. done ( function ( response ) {
$ ( "#output" ). text ( JSON . stringify (response, null , 2 ));
})
. fail ( function () {
$ ( "#output" ). text ( "Error fetching weather data." );
});
更正区分:data.dataType: "fnd"是发给HKO的API query parameter;外层dataType: "json"是jQuery对response的解析设置
URL按课件记录;本笔记未实际请求公开API,不把课件截图当作当前weather report
Failure and Cross-origin Requests
Failure可能来自HTTP error、网络失败、timeout、JSON parsing等,不等同于“server完全没有response”
补充:cross-origin request受browser policy约束,server需要适当CORS headers允许页面读取response;更换为jQuery不会自动绕过限制。MDN CORS
4.5 AJAX POST Request with jQuery
POST把data发给server处理,通常放在request body中
课件p.83的https://api.example.com/submit是dummy URL,不能当作可用submission endpoint
下例使用自己的/submitroute;没有对应backend时不能实际完成提交
const data = { name: "John Doe" , email: "john@example.com" };
$. post ( "/submit" , data)
. done ( function () { $ ( "#output" ). text ( "Data submitted successfully!" ); })
. fail ( function () { $ ( "#output" ). text ( "Error submitting data." ); });
Request Data vs Response Data
默认将普通object作为data传给$.post()时,通常编码为form-style key-value data,不自动发送JSON body
补充:需要JSON body时使用JSON.stringify(),并设置contentType;dataType仍表示期望的response format
const data = { name: "John Doe" , email: "john@example.com" };
$. ajax ({
url: "/submit" ,
method: "POST" ,
contentType: "application/json" ,
data: JSON . stringify (data),
dataType: "json"
})
. done ( function ( response ) { console. log (response); })
. fail ( function ( xhr , textStatus ) { console. log ( "Submit failed:" , textStatus); });
链接