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 StorageServer-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

Inspect in DevTools

  • 在浏览器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

  • 读取已保存value;更新时继续使用相同key
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

对比项CookiessessionStoragelocalStorage
APIdocument.cookie、HTTP headersStorage APIStorage API
主要scopedomain/path及相关cookie规则origin + tab/page sessionorigin
lifetimesession或指定expirypage 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

  • 要先加载jQuery,再运行调用$()的代码
<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()

  • 移除指定class,不删除element
$("#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()

  • 按键释放时触发;与keydown触发时机不同
$("#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 ObjectJSON 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

  • 对已有key重新赋值即可更新value
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); });

链接