JavaScript | 笔记

引言 Q&A 补充

动态加载 js, css

参考: 动态加载css,js,动态创建link标签,script标签_小龚的博客-CSDN博客_动态加载script标签 JS动态引入js、CSS动态创建script/link/style标签 - 岁月淡忘了谁 - 博客园 如何实现JavaScript动态加载CSS和JS文件_javascript技巧_脚本之家
utils.js
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
var utils = {};

utils.dynamicLoad = {
css: (href, onloadCallbackStr) => {
let loadStr = `
(function () {
var hm = document.createElement("link");
hm.href = "{{hbeSeoContent}}#123;href}";
// 注意: 需要设置以下两项, 浏览器才会加载
hm.setAttribute("rel", "stylesheet");
hm.setAttribute("type", "text/css");
hm.onload = () => {
{{hbeSeoContent}}#123;onloadCallbackStr};

// 加载完就删除
// hm.remove();
};
var heads = document.getElementsByTagName("head");
if(heads.length)
heads[0].appendChild(hm);
else
document.documentElement.appendChild(hm);
})();`;

return loadStr;
},

js: (src, onloadCallbackStr) => {
let loadStr = `
(function () {
var hm = document.createElement("script");
hm.src = "{{hbeSeoContent}}#123;src}";
hm.onload = () => {
{{hbeSeoContent}}#123;onloadCallbackStr};

// 加载完就删除
// hm.remove();
};
var heads = document.getElementsByTagName("head");
if(heads.length)
heads[0].appendChild(hm);
else
document.documentElement.appendChild(hm);
})();`;

return loadStr;
}
};

export default utils;

JavaScript 查找 注释节点: <!-- 注释节点值 -->

参考: 在整个body中找到所有的注释(通过JS来操作节点寻找)-CSDN社区
1
2
3
4
5
6
7
8
9
// 从 body 开始向内查找
var bodyElement = document.getElementsByTagName("body")[0];

for (var i=0; i<bodyElement.childNodes.length; i++) {
// 注释节点 类型 为 8
if (bodyElement.childNodes[i].nodeType == 8) {
console.log(bodyElement.childNodes[i].nodeValue);
}
}
但是这个只能找到body的子节点是注释的情况,
当然,就这样一直循环下去也可以找到body子节点的子节点是注释的情况,但是如果在很深的DOM节点里,用这种循环的方式就很不好书写。
所以我想到用递归来写
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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div>
<div>
<div>
<!-- note1 -->
</div>
</div>
<div>
<div></div>
</div>
</div>
<div>
<div>
<p>
<!-- note2 -->
<p>
</div>
<div>
<div></div>
</div>
</div>
<!-- note3 -->
<div>
<div>
<div></div>
</div>
<!-- note4 -->
<div>
<div></div>
</div>
</div>
<script type="text/javascript">
function eachComment(ele) {
for (var i=0; i<ele.childNodes.length; i++) {
var child = ele.childNodes[i];
if (child.nodeType == 8) {
console.log(child.nodeValue);
} else if (child.childNodes) {
eachComment(child);
}
}
}
var bodyElement = document.getElementsByTagName("body")[0];
eachComment(bodyElement);
</script>
</body>
</html>
注意: <!-- PluginCore.IPlugins.IPluginWidget --> 用 console.log(child.nodeValue); 得到的值 是 PluginCore.IPlugins.IPluginWidget 两边有空格,
也 nodeValue 就是内部内容, 不会去除两边空格的

JavaScript Element.replaceWith(...nodes)

参考: 在JS中使用replaceWith将元素替换为DOMstring或多个元素 - 我爱学习网 Element.replaceWith() - Web API 接口参考 | MDN Element.replaceWith() - Web APIs | MDN - 英文 参考 更详细

补充

经过测试, 注释节点也可以用
1
2
3
4
5
6
7
const span = document.createElement("span");
注释节点.replaceWith(span)
// 注意: 注释节点不变, 注释节点 依然指向原来的 , 而不是 replaceWith 后的
// 注意: 经测试: 不同节点.replaceWith(node1) 时, node1 对象不要重复使用, 否则可能导致想象外效果

temp1.replaceWith("<h1>aaa</h1>")
// 这样会替换为 nodeValue: '<h1>aaa</h1>' 的文本节点
正确做法
1
2
3
c = parseDom("<h1>ccc</h1>")
// 注意: 不要省略 "..."
temp4.replaceWith(...c)

语法

我可以使用replaceWith将任意一个子跨度与多个元素和文本节点交换吗 Element.replaceWith()的签名接受数量可变的Node或DOMString参数。。。

Syntax

1
replaceWith(...nodes)

案例

Using replaceWith()

1
2
3
4
5
6
7
8
9
var parent = document.createElement("div");
var child = document.createElement("p");
parent.appendChild(child);
var span = document.createElement("span");

child.replaceWith(span);

console.log(parent.outerHTML);
// "<div><span></span></div>"

Browser compatibility

Node.nodeType

参考: Node.nodeType - Web API 接口参考 | MDN 只读属性 Node.nodeType 表示的是该节点的类型。 nodeType 属性可用来区分不同类型的节点,比如 元素, 文本注释
1
var type = node.nodeType;
返回一个整数,其代表的是节点类型。其所有可能的值请参考 节点类型常量.

节点类型常量

常量 描述
Node.ELEMENT_NODE 1 一个 元素 节点,例如 ](https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/p) 和 [
Node.TEXT_NODE 3 Element 或者 Attr 中实际的 文字
Node.CDATA_SECTION_NODE 4 一个 CDATASection,例如 <!CDATA[[ … ]]>。
Node.PROCESSING_INSTRUCTION_NODE 7 一个用于XML文档的 ProcessingInstruction (en-US) ,例如 <?xml-stylesheet ... ?> 声明。
Node.COMMENT_NODE 8 一个 Comment 节点。
Node.DOCUMENT_NODE 9 一个 Document 节点。
Node.DOCUMENT_TYPE_NODE 10 描述文档类型的 DocumentType 节点。例如 <!DOCTYPE html> 就是用于 HTML5 的。
Node.DOCUMENT_FRAGMENT_NODE 11 一个 DocumentFragment 节点

例子

不同的节点类型
1
2
3
4
5
6
7
8
9
10
11
document.nodeType === Node.DOCUMENT_NODE; // true
document.doctype.nodeType === Node.DOCUMENT_TYPE_NODE; // true

var fragment = document.createDocumentFragment();
fragment.nodeType === Node.DOCUMENT_FRAGMENT_NODE; // true

var p = document.createElement("p");
p.textContent = "很久很久以前...";

p.nodeType === Node.ELEMENT_NODE; // true
p.firstChild.nodeType === Node.TEXT_NODE; // true
注释
1
2
3
4
// 该示例会检查 document 下第一个节点是不是注释,如果不是,则会提醒。
var node = document.documentElement.firstChild;
if (node.nodeType != Node.COMMENT_NODE)
console.log("你应该认真编写代码注释!");

Element.remove()

删除 Element 节点

字符串转 DOM 对象

参考: javascript转换字符串为dom对象(字符串动态创建dom)_javascript技巧_脚本之家
1
2
3
4
5
function parseDom(arg) {
   var objE = document.createElement("div");
   objE.innerHTML = arg;
   return objE.childNodes;
};

axios

参考: Axios axios/axios: Promise based HTTP client for the browser and node.js Promise based HTTP client for the browser and node.js Using npm:
1
npm install axios
Using unpkg CDN:
1
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
1
const axios = require('axios');

Axios API

1
2
3
4
5
6
7
8
9
// Send a POST request
axios({
method: 'post',
url: '/user/12345',
data: {
firstName: 'Fred',
lastName: 'Flintstone'
}
});
1
2
3
4
5
6
7
8
9
// GET request for remote image in node.js
axios({
method: 'get',
url: 'http://bit.ly/2mTM3nY',
responseType: 'stream'
})
.then(function (response) {
response.data.pipe(fs.createWriteStream('ada_lovelace.jpg'))
});

The Axios Instance

1
2
3
4
5
const instance = axios.create({
baseURL: 'https://some-domain.com/api/',
timeout: 1000,
headers: {'X-Custom-Header': 'foobar'}
});

Request Config

1
2
3
4
// `responseType` indicates the type of data that the server will respond with
// options are: 'arraybuffer', 'document', 'json', 'text', 'stream'
// browser only: 'blob'
responseType: 'json', // default

Config Defaults

Global axios defaults
1
2
3
axios.defaults.baseURL = 'https://api.example.com';
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
Custom instance defaults
1
2
3
4
5
6
7
8
9
10
// Set config defaults when creating the instance
const instance = axios.create({
baseURL: 'https://api.example.com'
});

// Alter defaults after instance has been created
instance.defaults.headers.common['Authorization'] = AUTH_TOKEN;

// 注意: timeout 的单位 是 ms, 因此这里是 2.5 seconds
instance.defaults.timeout = 2500;

axios 例子: request.js

utils/request.js
request.js
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
import axios from "axios";
import qs from "qs";

// build 环境
// axios.defaults.baseURL = 'http://api.tikotiko.fun';
// axios.defaults.baseURL = 'http://localhost:4530/';
axios.defaults.baseURL = process.env.VUE_APP_API;

// axios 请求拦截 - 在发送请求之前做某件事
axios.interceptors.request.use(
function(request) {
// 解决跨域 post 变 OPTIONS,导致 404
if (request.method === "post") {
request.headers["Content-Type"] =
"application/x-www-form-urlencoded;charset=UTF-8";
request.data = qs.stringify({
...request.data
});
}

// 如果有登录状态token的话,则添加到请求头
if (localStorage.token) {
// 在 headers 中设置 Authorization 属性放token,token是存在缓存中的
request.headers.Authorization = `Bearer {{hbeSeoContent}}#123;localStorage.token}`;
}

return request;
},
function(error) {
return Promise.reject(error);
}
);

/**
* 发送http请求
* @param {String} url 目标rl
* @param {String} method 方法get,post,put etc.
* @param {Object} params 对象参数
* @returns {Promise<unknown>}
*/
const request = function(url, method, params) {
return new Promise((resolve, reject) => {
if (method == "get") {
axios({
url: url,
method: "get",
params: params
})
.then(res => {
res.data.status = res.status;
resolve(res.data);
})
.catch(err => {
err.data.status = err.status;
reject(err.data);
});
} else {
axios({
url: url,
method: method,
data: params
})
.then(res => {
res.data.status = res.status;
resolve(res.data);
})
.catch(err => {
err.data.status = err.status;
reject(err.data);
});
}
});
};

export default request;
使用 request.js api/plugins.js
1
2
3
4
5
import request from "@/utils/request.js";

export page => request("/article/last", "get", { page: page });

export pluginId => request("/plugincore/admin/plugins/install", "post", { pluginId: pluginId });

axios 例子: 请求 html 字符串

参考: axios里的responseType - 简书 关于axios 的responseType类型的设置 - 鲁班快跑 - 博客园
1

responseType

XMLHttpRequest

XMLHttpRequest 本身就是支持responseType "" responseType 为空字符串时,采用默认类型 DOMString,与设置为 text 相同。 arraybuffer response 是一个包含二进制数据的 JavaScript ArrayBuffer。 blob response 是一个包含二进制数据的 Blob 对象 。 document response 是一个 HTML Document 或 XML XMLDocument,这取决于接收到的数据的 MIME 类型。请参阅 XMLHttpRequest 中的 HTML 以了解使用 XHR 获取 HTML 内容的更多信息。 json response 是一个 JavaScript 对象。这个对象是通过将接收到的数据类型视为 JSON 解析得到的。 text response 是一个以 DOMString 对象表示的文本。 ms-stream response 是下载流的一部分;此响应类型仅允许下载请求,并且仅受 Internet Explorer 支持。 补充: 默认 xhr 请求 会 自动 带上 Cookie

动态创建 script

    F12 直接修改html页面加script标签,这样方式浏览器只会解析,不会执行js, network中也没有加载 script.src F12 Console 通过执行 js createElement 动态创建 script 标签形式,这种方式就能加载js并执行
补充: 动态插入JS可以不阻碍 DOMContentLoad

前端存储 cookie、sessionStorage、localStorage、websql、indexeddb

参考: 前端存储cookie、sessionStorage、localStorage、websql与indexeddb_奋斗的小猪-CSDN博客 Cookie是不可以跨域名的,隐私安全机制禁止网站非法获取其他网站的Cookie。 正常情况下,同一个一级域名下的两个二级域名也不能交互使用Cookie,比如test1.mcrwayfun.com和test2.mcrwayfun.com,因为二者的域名不完全相同。如果想要mcrwayfun.com名下的二级域名都可以使用该Cookie,需要设置Cookie的domain参数为.mcrwayfun.com,这样使用test1.mcrwayfun.com和test2.mcrwayfun.com就能访问同一个cookie。 一级域名又称为顶级域名,一般由字符串+后缀组成。熟悉的一级域名有baidu.com,qq.com。com,cn,net等均是常见的后缀。
二级域名是在一级域名下衍生的,比如有个一级域名为mcrfun.com,则blog.mcrfun.com和www.mcrfun.com均是其衍生出来的二级域名。 path属性决定允许访问Cookie的路径。比如,设置为"/"表示允许所有路径都可以使用Cookie。

sessionStorage

访问限制性

不同于cookie,sessionStorage的访问限制更高一些,只有当前设定sessionStorage的域下才能访问,而且不同的两个tab之间不能互通。例,我在www.qq.com下种下了sessionStorage,在wx.qq.com下是,无法访问的; 在新开的tab下,或者关闭本TAB再打开后(也是www.qq.com),也是无法访问到之前种的sessionStorage的; 而本tab刷新的时候,sessionStorage确是可以访问的。 以上种种,证明sessionStorage里面的session,并不同于cookie,是以tab为级别的session。

localStorage

访问的限制性

localStorage与sessionStorage虽然相似,但是访问限制却不尽相同,localStorage的访问域默认设定为设置localStorage的当前域,其他域名不可以取。
这点与sessionStorage相同,但是与sessionStorage不同的是,localStorage设定后,新开tab是可以访问到的。

存储时间

localStorage理论上讲是 永久性质的存储。但是,免不了用户会使用浏览器清除数据,或者浏览器有时候为了节省,去清除数据。

websql 与 indexeddb

参考 感谢帮助!