DOM-CheatSheet
DOM CheatSheet | DOM 语法速览与实践清单
const bodyRect = document.body.getBoundingClientRect(),
elemRect = element.getBoundingClientRect(),
offset = elemRect.top - bodyRect.top;
console.log("Element is " + offset + " vertical pixels from <body>");
// 获取 StyleSheet 对象
var sheets = document.styleSheets; // returns an Array-like StyleSheet List
var sheet = document.styleSheets[0];
// 添加样式规则
sheet.insertRule("header { float: left; opacity: 0.8; }", 1);
界面事件
事件监听
function ready(callback) {
...
var state = document.readyState;
if (state === 'complete' || state === 'interactive') {
return setTimeout(callback, 0);
}
document.addEventListener('DOMContentLoaded', function onLoad() {
callback();
});
}
The DOM of the referenced element is not part of the DOM of the referencing HTML page. It has isolated style sheets.
在拖拽等场景中我们还需要处理 mousedown
与 touchstart
等事件,
事件传播与委托
Capturing Phase | 捕获阶段:自顶向下传递到目标元素Target Phase | 目标阶段:事件达到目标元素Bubbling Phase | 冒泡阶段:事件自底向上传递
下图以<td>
元素为例,指明了事件传播的顺序:

鉴于捕获阶段很少被提及,我们主要会讨论冒泡阶段;对于常见的 addEventListener
设置监听器函数而言,如果其第三个参数设置为
// document.addEventListener("click", delegate(buttonsFilter, buttonHandler));
function delegate(criteria, listener) {
return function (e) {
var el = e.target;
do {
if (!criteria(el)) {
continue;
}
e.delegateTarget = el;
listener.call(this, e);
return;
} while ((el = el.parentNode));
};
}
滚动事件
在
如果
针对这个特性,我们往往在window.addEventListener('scroll', handler, true)
以避免所谓的滚动失效。
自定义事件
const event = new Event("build");
// Listen for the event.
elem.addEventListener(
"build",
function (e) {
/* ... */
},
false
);
// Dispatch the event.
elem.dispatchEvent(event);
路由
const stateObj = { foo: "bar" };
// 将当前 URL 替换为 http://mozilla.org/bar.html
history.pushState(stateObj, "page 2", "bar.html");
浏览器也为我们提供了
if ("onhashchange" in window) {
//no alert
console.log("The browser supports the hashchange event!");
}
function locationHashChanged() {
if (location.hash === "#somecoolfeature") {
somecoolfeature();
}
}
window.onhashchange = locationHashChanged;
网络通信
XMLHTTPRequest
var xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.responseType = "json";
xhr.onload = function () {
console.log(xhr.response);
};
xhr.onerror = function () {
console.log("Booo");
};
xhr.send();
Fetch
fetch("./file.json")
.then((response) => {
response.json().then((data) => {
console.log(data);
});
})
// 使用 catch 方法容错
.catch((err) => {
console.error(err);
});
Request
// Headers 对象用于设置请求头
const headers = new Headers();
headers.append("Content-Type", "application/json");
const request = new Request("./file.json", {
headers: new Headers({ "Content-Type": "application/json" }),
});
fetch(request);
简单的
const options = {
method: "post",
headers: {
"Content-type": "application/x-www-form-urlencoded; charset=UTF-8",
},
body: "foo=bar&test=1",
};
fetch(url, options).catch((err) => {
console.error("Request failed", err);
});
发起response.type
属性,其有basic
,并且不会对响应的读取有任何限制。当发起的是跨域请求,并且响应头中包含了 CORS 相关设置,那么响应类型会被标识为Cache-Control
Content-Language
Content-Type
Expires
Last-Modified
Pragma
这些响应头字段。最后,
针对响应类型的不同,我们也可以在请求体中预设不同的请求模式,以加以过滤:
-
same-origin: 仅针对同域请求有效,拒绝其他域的请求。 -
cors: 允许针对同域或者其他访问正确的CORS 头的请求。 -
cors-with-forced-preflight: 每次请求前都会进行预检。 -
no-cors: 针对那些未返回CORS 头的域发起请求,并且返回opaque 类型的响应,并不常用。
参照上文,
fetch("http://some-site.com/cors-enabled/some.json", { mode: "cors" });
Response
fetch("./file.json").then((response) => {
// 返回状态码
console.log(response.status);
// 状态描述
console.log(response.statusText);
// 101, 204, 205, or 304 is a null body status
// 200 to 299, inclusive, is an OK status (success)
// 301, 302, 303, 307, or 308 is a redirect
console.log(response.headers.get("Content-Type"));
console.log(response.headers.get("Date"));
});
对于响应体,则需要使用内置的
// 根据响应头决定读取方式
fetch(url).then(function(response) {
if (response.headers.get('Content-Type') === 'application/json') {
return response.json();
}
return response.text();
});
// 内置流读取函数
.arrayBuffer()
.blob()
.formData()
.json()
.text()
相较于
self.addEventListener("fetch", function (event) {
event.respondWith(
fetch("video.unknowncodec").then(function (response) {
var h264Stream = response.body
.pipeThrough(codecDecoder)
.pipeThrough(h264Encoder);
return new Response(h264Stream, {
headers: { "Content-type": "video/h264" },
});
})
);
});
fetch(url).then(function (response) {
return response.json().catch(function () {
// This does not work:
return response.text();
});
// 修改为
// return response.clone().json()...
});
跨域请求
跨域只是限制不同域下的数据接收,跨域提交数据有些时候是可以的,但会收到浏览器的报错,此时数据已经提交到服务器,但浏览器拒绝接收服务器的返回,这时就存在跨域问题,你不知道服务器对你的数据到底做了些什么,他可能没收到(网络原因
CORS
本地通信
跨
同源通信
- BroadcastChannel
const channel = new BroadcastChannel("channel-name");
channel.postMessage("some message");
channel.postMessage({ key: "value" });
channel.onmessage = function (e) {
const message = e.data;
};
channel.close();
- SharedWorker API
// main.js
const worker = new SharedWorker("shared-worker.js");
worker.port.postMessage("some message");
worker.port.onmessage = function (e) {
const message = e.data;
};
// shared-worker.js
const connections = [];
onconnect = function (e) {
const port = e.ports[0];
connections.push(port);
};
onmessage = function (e) {
connections.forEach(function (connection) {
if (connection !== port) {
connection.postMessage(e.data);
}
});
};
- Local Storage
localStorage.setItem("key", "value");
window.onstorage = function (e) {
const message = e.newValue; // previous value at e.oldValue
};
跨域通信
postMessage
数据存储
本地存储
Cookie
localStorage & sessionStorage
indexdb
文件操作
const debug = { hello: "world" };
let blob = new Blob([JSON.stringify(debug, null, 2)], {
type: "application/json",
});
// Blob(22) {size: 22, type: "application/json"}
// 也可以转化为类 URL 格式
const url = URL.createObjectURL(blob);
// "blob:https://developer.mozilla.org/88c5b6de-3735-4e02-8937-a16cc3b0e852"
// 设置自定义的样式类
blob = new Blob(["body { background-color: yellow; }"], {
type: "text/css",
});
link = document.createElement("link");
link.rel = "stylesheet";
//createObjectURL returns a blob URL as a string.
link.href = URL.createObjectURL(blob);
其他的类型转化为
const content = '<a id="a"><b id="b">hey!</b></a>'; // the body of the new file...
const blob = new Blob([content], { type: "text/xml" });
formData.append("webmasterfile", blob);
const BYTES_PER_CHUNK = 1024 * 1024; // 每个文件切片大小定为1MB .
const blob = document.getElementById("file").files[0];
const slices = Math.ceil(blob.size / BYTES_PER_CHUNK);
const blobs = [];
Array.from({ length: slices }).forEach(function (item, index) {
blobs.push(blob.slice(index, index + 1));
});
这里我们使用的
const reader = new FileReader();
reader.addEventListener("loadend", function () {
// reader.result 包含了 Typed Array 格式的 Blob 内容
});
reader.readAsArrayBuffer(blob);
blob = new Blob(["This is my blob content"], { type: "text/plain" });
read.readAsText(bolb); // 读取为文本
// reader.readAsArrayBuffer //将读取结果封装成 ArrayBuffer,如果想使用一般需要转换成 Int8Array或DataView
// reader.readAsBinaryString // 在IE浏览器中不支持改方法
// reader.readAsTex // 该方法有两个参数,其中第二个参数是文本的编码方式,默认值为 UTF-8
// reader.readAsDataURL // 读取结果为DataURL
// reader.readyState // 上传中的状态
在图片上传中,我们常常需要获取到本地图片的预览,参考 antd/Upload 中的处理:
// 将文件读取为 DataURL
const previewFile = (file: File, callback: Function) => {
const reader = new FileReader();
reader.onloadend = () => callback(reader.result);
reader.readAsDataURL(file);
};
// 设置文件的 DataUrl
previewFile(file.originFileObj, (previewDataUrl: string) => {
file.thumbUrl = previewDataUrl;
});
// JSX
<img src={file.thumbUrl || file.url} alt={file.name} />;
另一个常用的场景就是获取剪贴板中的图片,并将其预览展示,可以参考 coding-snippets/image-paste:
const cbd = e.clipboardData;
const fr = new FileReader();
for (let i = 0; i < cbd.items.length; i++) {
const item = cbd.items[i];
if (item.kind == "file") {
const blob = item.getAsFile();
if (blob.size === 0) {
return;
}
previewFile(blob);
}
}
标准的
// 仅可用于 Chrome 浏览器中
window.requestFileSystem =
window.requestFileSystem || window.webkitRequestFileSystem;
window.requestFileSystem(type, size, successCallback, opt_errorCallback);
简单的文件创建与写入如下所示:
function onInitFs(fs) {
fs.root.getFile(
"log.txt",
{ create: true },
function (fileEntry) {
// Create a FileWriter object for our FileEntry (log.txt).
fileEntry.createWriter(function (fileWriter) {
fileWriter.onwriteend = function (e) {
console.log("Write completed.");
};
fileWriter.onerror = function (e) {
console.log("Write failed: " + e.toString());
};
// Create a new Blob and write it to log.txt.
var blob = new Blob(["Lorem Ipsum"], { type: "text/plain" });
fileWriter.write(blob);
}, errorHandler);
},
errorHandler
);
}
window.requestFileSystem(window.TEMPORARY, 1024 * 1024, onInitFs, errorHandler);