目录
小朋友们你们好啊,我是甲骨文,Leon 被我处决了,荷修被我赶走了,整个寄呕吸蒽已经是我的天下了,你们准备受死吧啊哈哈哈哈哈哈哈哈哈哈😈😈😈。
上面的是一个很基础的选项按钮,我们可以在这之上制作出 这样 的效果1。
毫无疑问的是,这里运用了 Wikidot 的 HTML 模块。
[[html]]
[[/html]]
HTML 是一种 超文本标记语言 ,在网页制作中经常用到。当然,这里没必要展开叙述,只要了解一下就好。
现在我们要插入一个 HTML 模块:
[[html]]
<body>
<p>这里是一行测试用的文字。</p>
</body>
[[/html]]
其中:
- <body>: 【主体】标签,页面的内容都需要放在这里面。
- <p>: 【段落】标签,声明一段文字。
- </p>: 前面携带【/】的标签为反标签,代表标签声明内容的结束,此处代表【段落】标签结束。
- </body>: 同上,代表【主体】标签结束。
与 Wikidot 一样,HTML 也拥有 加粗 、倾斜 、删除线 和 下划线 。
[[html]]
<body>
<p><b>加粗</b> 、<i>倾斜</i> 、<s>删除线</s> 和 <u>下划线</u> 。</p>
</body>
[[/html]]
其中:
- <b>: 【加粗】标签。
- <i>: 【倾斜】标签。
- <s>: 【删除线】标签。
- <u>: 【下划线】标签。
相应的,HTML 也拥有标题、分割线等内容,碍于篇幅,此处不做演示。
本文一开始的按钮选项是怎样实现的呢?首先我们需要一个按钮。
[[html]]
<body>
<button>这是一个按钮</button>
</body>
[[/html]]
其中:
- <button>: 【按钮】元素,此处按钮显示的内容为【这是一个按钮】。
咦,为什么这个按钮看上去这么 【原始】?
这是没有 样式表 导致的。
那什么是样式表呢?我们在 Wikidot 中使用的版式就是样式表。
[[html]]
<head>
<link href="https://goc-sandbox-cn.wikidot.com/admin:themes/code/1" rel="stylesheet" type="text/css" />
</head>
<body>
<button>这是一个按钮</button>
</body>
[[/html]]
其中:
- <head>: 【头部】标签,用于声明页面信息。一般情况下,源标签、样式表都会放在此处。
- <link />:【外部链接】标签,用于引入外部内容。值得注意的是,该标签的【/】就在自身当中,而并没有单独的反标签,这样的标签被称为【单标签】,<br />(换行)、<hr />(分割线)等标签也是单标签。
- href: 【URL】属性,用于声明链入内容的URL地址。此处声明的地址是本沙盒站的版式代码地址。
- rel: 【关系】属性,用于声明链入内容与页面的关系。此处的 stylesheet 就代表其为本页面的样式表,此外还有 icon(页面图标)等。
- type: 【类型】属性,声明链入内容的属性,可有可无。此处的 text/css 就代表其为 CSS 样式表。
啊哈,就像页面本身的内容一样了,并没那么突兀。
接下来就要为其添加功能了。
[[html]]
<head>
<link href="https://goc-sandbox-cn.wikidot.com/admin:themes/code/1" rel="stylesheet" type="text/css" />
</head>
<body>
<button onclick="putWindow()">这是一个按钮</button>
<script type="text/javascript">
function putWindow() {
alert("弹出一段文字")
}
</script>
</body>
[[/html]]
其中:
- onclick: 【点击】事件,用于绑定点击后的行为。此处绑定了后文中的 putWindow() 方法。
- <script>: 【JavaScript】标签,用于声明 JavaScript 脚本。网页的行为都是由 JavaScript 决定的。
- type: 【类型】属性,声明链入内容的属性,可有可无。此处的 text/script 就代表其为 JavaScript 脚本。
- function: 声明一个【方法】,也即一套行为。此处声明的方法名为 putWindow ,其可以自行定义,但不能是 JavaScript 中原有的方法,如 alert 方法。
- alert(): 生成一个【弹窗提示】,内容在括号中定义,此处定义为【弹出一段文字】。
不出意外的话,浏览器弹出了一个提示,内容为【弹出一段文字】,对吧?
那么怎样生成一段新的文字呢?
[[html]]
<head>
<link href="https://goc-sandbox-cn.wikidot.com/admin:themes/code/1" rel="stylesheet" type="text/css" />
</head>
<body>
<button onclick="putWindow()">这是一个按钮</button>
<script type="text/javascript">
function putWindow() {
let h1 = document.createElement("h1")
h1.append("生成一号标题")
document.getElementsByTagName("body")[0].append(h1)
}
</script>
</body>
[[/html]]
其中:
- let: 【临时变量】,此处声明的变量名为【h1】。
- document: 【页面根节点】。这里没必要展开叙述,了解一下就好。
- createElement: 创建一个元素。此处创建的元素为【h1】,和变量名的【h1】不同,此处的 h1 指的是 h1 标签,也即【一号标题】标签,同理也可以换成 h2(二号标题)、h3(三号标题)……直到 h6(六号标题),以及其它什么标签,如 p(段落)。
- append(): 向节点追加内容,代码中第一次出现时向 h1 变量中追加了【生成一号标题】,而第二次出现时则为页面追加了 h1 变量。
- getElementsByTagName(): 根据标签名获取元素,此处为获取【主体】标签元素。此外还有 getElementById(根据 id 获取元素)、getElementsByClassName(根据类名获取元素)。
- [0]: 数组【下标】,代表数组的第N个元素(从0开始为第一个,1为第二个,以此类推)。此处为获取第一个【主体】标签元素。
现在可以在页面中生成新的文字了。
如果希望按钮在点击后消失,该怎样做呢?
[[html]]
<head>
<link href="https://goc-sandbox-cn.wikidot.com/admin:themes/code/1" rel="stylesheet" type="text/css" />
</head>
<body>
<button onclick="putWindow()" id="a_button">这是一个按钮</button>
<script type="text/javascript">
function putWindow() {
document.getElementById("a_button").remove()
let h1 = document.createElement("h1")
h1.append("生成一号标题")
document.getElementsByTagName("body")[0].append(h1)
}
</script>
</body>
[[/html]]
其中:
- id: 元素的【唯一标识】,此处为按钮定义的 id 为【a_button】。
- getElementById(): 与前文提到的 getElementsByTagName 类似,但此处为【根据 id 获取元素】。这里获取了 id 为 【a_button】的元素,也即我们添加的按钮。
- remove(): 删除页面元素,此处删除了我们添加的按钮。
还不错。那么现在可以试着制作一个类似本文开头的示例的选项按钮了。
[[html]]
<head>
<link href="https://goc-sandbox-cn.wikidot.com/admin:themes/code/1" rel="stylesheet" type="text/css" />
</head>
<body>
<button onclick="putWindow()" id="a_button">这是一个按钮</button>
<button onclick="putWindow2()" id="b_button">B</button>
<script type="text/javascript">
function putWindow() {
document.getElementById("a_button").remove()
document.getElementById("b_button").remove()
let h1 = document.createElement("h1")
h1.append("生成一号标题")
document.getElementsByTagName("body")[0].append(h1)
}
function putWindow2() {
document.getElementById("a_button").remove()
document.getElementById("b_button").remove()
let h2 = document.createElement("h2")
h2.append("生成二号标题")
document.getElementsByTagName("body")[0].append(h2)
}
</script>
</body>
[[/html]]
当然了,这样的写法实在繁琐,因而我们要善于运用 变量 和 方法 :
[[html]]
<head>
<link href="https://goc-sandbox-cn.wikidot.com/admin:themes/code/1" rel="stylesheet" type="text/css" />
</head>
<body>
<button onclick="putWindow()" id="a_button">这是一个按钮</button>
<button onclick="putWindow2()" id="b_button">B</button>
<script type="text/javascript">
var page_body = document.getElementsByTagName("body")[0]
var a_button = document.getElementById("a_button")
var b_button = document.getElementById("b_button")
function remove_ab() {
a_button.remove()
b_button.remove()
}
function putWindow() {
remove_ab()
let h1 = document.createElement("h1")
h1.append("生成一号标题")
page_body.append(h1)
}
function putWindow2() {
remove_ab()
let h2 = document.createElement("h2")
h2.append("生成二号标题")
page_body.append(h2)
}
</script>
</body>
[[/html]]
像这里,我们为【主体】和两个按钮单独定义了【全局变量】,方便后续维护。此外还为删除两个按钮的行为单独声明了一个方法,以在需要时调用,起到简化代码的作用。
- var: 【全局变量】。其与【临时变量】的区别在于,临时变量只能在自己所在的方法内调用,而全局变量可以在整个 JavaScript 脚本中调用。
到现在,每次选择只能生成一段文字,那么能不能多生成几段呢?当然可以!只需要创建一个数组就好。
[[html]]
<head>
<link href="https://goc-sandbox-cn.wikidot.com/admin:themes/code/1" rel="stylesheet" type="text/css" />
</head>
<body>
<button onclick="putWindow()" id="a_button">这是一个按钮</button>
<button onclick="putWindow2()" id="b_button">B</button>
<script type="text/javascript">
var page_body = document.getElementsByTagName("body")[0]
var a_button = document.getElementById("a_button")
var b_button = document.getElementById("b_button")
var ae_text = new Array()
function remove_ab() {
a_button.remove()
b_button.remove()
}
function putWindow() {
remove_ab()
ae_text.push("A第一段")
ae_text.push("A第二段")
ae_text.push("A第三段")
ae_text.push("A第四段")
for (let i = 0;i < ae_text.length;i++) {
let p = document.createElement("p")
p.innerHTML = ae_text[i]
page_body.append(p)
}
}
function putWindow2() {
remove_ab()
ae_text.push("B第一段")
ae_text.push("B第二段")
ae_text.push("B第三段")
ae_text.push("B第四段")
for (let i = 0;i < ae_text.length;i++) {
let p = document.createElement("p")
p.innerHTML = ae_text[i]
page_body.append(p)
}
}
</script>
</body>
[[/html]]
其中:
- new: 新建一个对象,此处新建的对象类型为 Array 。
- Array(): 数组类型对象。
- push(): 向数组中追加内容,内容在括号中定义。
- for(): 循环方法,重复执行大括号中的内容。
- let i = 0: 定义临时变量 i 为0。
- i < ae_text.length: 当 i 小于 ae_text.length 时执行大括号中的内容,也即,当 i 大于或等于 ae_text.length 时结束循环。
- length: Array 对象的长度,即数组中元素的数量(此处为4)。
- i++: 每完成一次循环时,i 的值加1;
- innerHTML: 元素中的内容。与之类似的还有 innerText ,区别在于,前者能够获取元素中嵌套的子元素,而后者只能获取文字。此处将 p 变量的内容设置为了 ae_text 的第 i 变量个元素。
现在我们成功地生成了多段文字,但新的问题也接踵而至:生成的文字被遮挡住了一部分 。
事实上,这主要是由于 Wikidot 的 HTML 模块本质上是在原有的页面中嵌入了一个新页面,而不是直接加入原有的页面,因此,它的高度会严重影响其内容的显示效果。
修复的办法也很简单粗暴:设置 HTML 模块的最小高度。
[[html]]
<head>
<link href="https://goc-sandbox-cn.wikidot.com/admin:themes/code/1" rel="stylesheet" type="text/css" />
<style type="text/css">
body {
min-height: 10rem;
}
</style>
</head>
<body>
<button onclick="putWindow()" id="a_button">这是一个按钮</button>
<button onclick="putWindow2()" id="b_button">B</button>
<script type="text/javascript">
var page_body = document.getElementsByTagName("body")[0]
var a_button = document.getElementById("a_button")
var b_button = document.getElementById("b_button")
var ae_text = new Array()
function remove_ab() {
a_button.remove()
b_button.remove()
}
function putWindow() {
remove_ab()
ae_text.push("A第一段")
ae_text.push("A第二段")
ae_text.push("A第三段")
ae_text.push("A第四段")
for (let i = 0;i < ae_text.length;i++) {
let p = document.createElement("p")
p.innerHTML = ae_text[i]
page_body.append(p)
}
}
function putWindow2() {
remove_ab()
ae_text.push("B第一段")
ae_text.push("B第二段")
ae_text.push("B第三段")
ae_text.push("B第四段")
for (let i = 0;i < ae_text.length;i++) {
let p = document.createElement("p")
p.innerHTML = ae_text[i]
page_body.append(p)
}
}
</script>
</body>
[[/html]]
其中:
- <style>: 【内嵌样式】标签,用于定义页面的 CSS 样式,优先级高于【外部链接】标签。
- type: 【类型】属性,声明链入内容的属性,可有可无。此处的 text/css 就代表其为 CSS 样式表。
- body: 【标签选择器】,用于选择特定标签,并在大括号中定义其样式。此处选择了【主体】标签。
- min-height: 【最小高度】属性,设定最小高度。此处设定为10 rem 。
- rem: 【根文字大小】,即页面根节点的文字大小,一般为16 px(像素)。与之类似的还有 em ,即当前节点文字大小。
如果想要文字以动画形式显示,则可以为其添加 CSS 样式。
[[html]]
<head>
<link href="https://goc-sandbox-cn.wikidot.com/admin:themes/code/1" rel="stylesheet" type="text/css" />
<style type="text/css">
body {
min-height: 10rem;
}
p {
margin-top: 1rem;
animation: text_input 1s;
}
@keyframes text_input {
0% {margin-top: 10rem;}
100% {margin-top: 1rem;}
}
</style>
</head>
<body>
<button onclick="putWindow()" id="a_button">这是一个按钮</button>
<button onclick="putWindow2()" id="b_button">B</button>
<script type="text/javascript">
var page_body = document.getElementsByTagName("body")[0]
var a_button = document.getElementById("a_button")
var b_button = document.getElementById("b_button")
var ae_text = new Array()
function remove_ab() {
a_button.remove()
b_button.remove()
}
function putWindow() {
remove_ab()
ae_text.push("A第一段")
ae_text.push("A第二段")
ae_text.push("A第三段")
ae_text.push("A第四段")
for (let i = 0;i < ae_text.length;i++) {
let p = document.createElement("p")
p.innerHTML = ae_text[i]
page_body.append(p)
}
}
function putWindow2() {
remove_ab()
ae_text.push("B第一段")
ae_text.push("B第二段")
ae_text.push("B第三段")
ae_text.push("B第四段")
for (let i = 0;i < ae_text.length;i++) {
let p = document.createElement("p")
p.innerHTML = ae_text[i]
page_body.append(p)
}
}
</script>
</body>
[[/html]]
其中:
- margin-top:上方外边距,即元素与上方元素的间距。
- animation: 绑定动画,此处绑定的动画为 text_input ,动画时长为1秒。
- @keyframes: 声明动画,此处声明的动画名为 text_input 。
- 0%: 设定动画开始时的样式,后文中的100%同理为动画结束时的样式,此外也可以是任何数字(为动画运行进度)。
在了解了上文的内容后,我们就可以开始更进一步的学习了。
如你所见,如果选择【但是,我拒绝!】,就会进入 循环 ,直到选择【好】。这种效果在循环类型的作品中还算常见,例如 港口上的天空 就运用了它。
[[html]]
<head>
<link href="https://goc-sandbox-cn.wikidot.com/admin:themes/code/1" rel="stylesheet" type="text/css" />
</head>
<body>
<button onclick="a()" id="button1">好</button>
<button onclick="b()" id="button2">但是,我拒绝!</button>
<script type="text/javascript">
var body = document.getElementsByTagName("body")[0]
var b1 = document.getElementById("button1")
var b2 = document.getElementById("button2")
function removeButton() {
b1.remove()
b2.remove()
}
function a() {
removeButton()
let p = document.createElement("p")
p.append("这是正确的🤓🤓🤓")
body.append(p)
let b3 = document.createElement("button")
b3.append("确定")
b3.onclick = function() {
b3.remove()
}
body.append(b3)
}
function b() {
removeButton()
let p = document.createElement("p")
p.append("再给你一次机会")
body.append(p)
body.append(b1)
body.append(b2)
}
</script>
</body>
[[/html]]
经过前面的学习,我们不难找出这段代码的关键点在哪里。
- 多层按钮选项:
- 按下按钮后,创建一个新的按钮。
- 为新创建的按钮绑定点击事件。
- onclick: 【点击】事件。
- 将新创建的按钮追加至页面。
- 循环:
- 向页面追加新的文本。
- 将按钮重新追加至页面。
一个只能做选择题的互动页面是无聊的,因而我们要加入一些更有 代入感 的代码。
把“我爱学习”打在公屏上:
这里我们做了一件很有趣的事情:获取读者输入的内容 ,很多互动页面的【密钥】都是以这种方式实现的。
[[html]]
<head>
<link href="https://goc-sandbox-cn.wikidot.com/admin:themes/code/1" rel="stylesheet" type="text/css" />
</head>
<body>
<input type="text" id="text" placeholder="我爱学习" />
<button onclick="inputext()">确定</button>
<script type="text/javascript">
function inputext() {
let t = document.getElementById("text").value
if (t == "" || t == null) {
alert("你啥也不输入是吧(恼")
}
else if (t == "我爱学习") {
alert("很好!")
}
else {
alert(t + "……\n你要不要看看你在说什么?")
}
}
</script>
</body>
[[/html]]
其中:
- <input />: 表单【控件】,通过 type 选择控件类型。
- type: 【类型】属性,声明控件类型,此处为【单行文本输入框】类型,此外还包括但不限于:
- password: 【密码】类型。
- radio: 【单选框】类型。
- check: 【复选框】类型。
- number: 【数字】类型。
- 很多不那么常用的类型……
- placeholder: 【提示文本】属性,用于设定输入框的提示文本,此处设定为【我爱学习】。
- type: 【类型】属性,声明控件类型,此处为【单行文本输入框】类型,此外还包括但不限于:
- value: 获取元素的值,此处获取的是 document.getElementById("text") 的值,也即输入框的值。
- if: 条件语句,当括号内的值为【真】时,执行大括号中的内容。此处设定为,当 t(输入框的值)为空时执行。
- ==: 等于。类似的还有 <(小于)、 >(大于)、 <=(小于等于)、>=(大于等于)、!=(不等于)。
- ||: 或者。类似的还有 &&(并且)。
- else if: 条件语句,当此前条件为假,但此处括号中的条件为真时,执行大括号中的内容。此处设定为,当 t(输入框的值)为“我爱学习”时执行。
- else: 条件语句,当此前条件为假时执行。
某些情况下,我们希望页面能够记录读者的行为,例如某个按钮点击过几次、某个输入框输入过什么,这时候就可以运用 LocalStorage 实现 本地存储 。
LocalStorage 的结构像这样:
| 键 | 值 |
|---|---|
| 名字 | Leon |
| 年龄 | 1145 |
我们可以随时从浏览器中调用存储的数据。
我们可以点击几下按钮,接着刷新页面,或者退出并重新打开页面,这时的文字依然被保留。
[[html]]
<head>
<link href="https://goc-sandbox-cn.wikidot.com/admin:themes/code/1" rel="stylesheet" type="text/css" />
</head>
<body>
<p id="num"></p>
<button onclick="a()">点我</button>
<script type="text/javascript">
var numText = document.getElementById("num")
var numStorage = localStorage.getItem("Num")
if (numStorage == null) numText.innerHTML = "该按钮还没有被点击过。"
else numText.innerHTML = "该按钮被点击过" + numStorage + "次。"
function a() {
if (numStorage == null) localStorage.setItem("Num", 1)
else localStorage.setItem("Num", parseInt(numStorage) + 1)
numStorage = localStorage.getItem("Num")
numText.innerHTML = "该按钮被点击过" + numStorage + "次。"
}
</script>
</body>
[[/html]]
其中:
- 页面加载时:
- 定义全局变量 numStorage 并赋值为 localStorage.getItem("Num") 。
- localStorage: 【本地存储】,即保存在浏览器中的临时数据。
- getItem(): 从本地存储中获取数据的值,数据的键为括号中的内容,此处获取的是键为【Num】的数据的值。
- 当 numStorage 为空,也即浏览器当前没有存储键为 Num 的数据时,在段落中显示【该按钮还没有被点击过】。
- 否则在段落中显示【当前按钮被点击过 [numStorage] 次】。
- 定义全局变量 numStorage 并赋值为 localStorage.getItem("Num") 。
- 按钮点击时:
- 当 numStorage 为空,也即浏览器当前没有存储键为 Num 的数据时,在本地存储中创建键为【Num】的数据并赋值为【1】。
- setItem(): 从本地存储中设置数据的值,数据的键为括号中的第一条内容,值为括号中的第二条内容,若浏览器当前没有存储符合键的数据,则创建一条新的数据并赋值,此处创建的是键为【Num】、值为【1】的数据。
- 否则在本地存储中设置键为【Num】的数据的值为 numStorage 加1。
- parseInt: 将字符串转换为数字。localStorage 的键值均为字符串类型,进行数学计算前必须转换为数字类型。
- 将 numStorage 重新赋值为 localStorage.getItem("Num") 。
- 在段落中显示【当前按钮被点击过 [numStorage] 次】。
- 当 numStorage 为空,也即浏览器当前没有存储键为 Num 的数据时,在本地存储中创建键为【Num】的数据并赋值为【1】。
如果我们希望删除本地存储数据该怎么办?
[[html]]
<head>
<link href="https://goc-sandbox-cn.wikidot.com/admin:themes/code/1" rel="stylesheet" type="text/css" />
</head>
<body>
<p id="num"></p>
<button onclick="a()">点我</button>
<button onclick="b()">删除记录</button>
<script type="text/javascript">
var numText = document.getElementById("num")
var numStorage = localStorage.getItem("Num")
if (numStorage == null) numText.innerHTML = "该按钮还没有被点击过。"
else numText.innerHTML = "该按钮被点击过" + numStorage + "次。"
function a() {
if (numStorage == null) localStorage.setItem("Num", 1)
else localStorage.setItem("Num", parseInt(numStorage) + 1)
numStorage = localStorage.getItem("Num")
numText.innerHTML = "该按钮被点击过" + numStorage + "次。"
}
function b() {
localStorage.removeItem("Num")
location.reload()
}
</script>
</body>
[[/html]]
不是说一行吗?为什么多了这么多?其实其中只有一行是真正用于删除本地存储数据的,也就是:
- localStorage.removeItem("Num")
get 是获取,set 是设置,而 remove 则毫无疑问是删除了。与之类似的还有 localStorage.clear() ,其作用是【删除全部本地存储数据】。
值得一提的是,每个网站的 LocalStorage 是各自独立的,因而几乎不需要担心冲突问题(但同一个网站内则可能发生冲突)。
至于 location.reload() ,则是【重载页面】,也即刷新。
如果我们想写一篇关于超形上学实体的文档,那么 Meta 就是增加逼格的不可或缺的一环。以 SCP 基金会的【锥形长矛】为例,倘若我们既希望加入它的戏份,又觉得单纯的文字描写没什么代入感,那么就可以操纵评分利用 HTML 模块制作一个 虚假的评分模块 。
可以看见,当重复评分时,会做出提示;当 down 时,页面会被自动删除并显示空页面提示,除非取消评分。
最重要的是,这会被记录,即便刷新页面也不会消失 ,直到点击空页面提示中的【点击此处创建页面】,此处才会被重置。
在页面中使用这行代码来加入此功能:
[[include :goc-sandbox-cn:rate]]
当然,我们也可以让评分模块置于右侧,只需要这样:
[[include :goc-sandbox-cn:rate
| right=0
]]
[[html]]
<head>
<link href="https://goc-sandbox-cn.wikidot.com/admin:themes/code/1" rel="stylesheet" type="text/css" />
</head>
<body>
<!--↓评分模块↓-->
<div class="page-rate-widget-box"><span class="rate-points">评分: <span id="rate-num"></span></span><span class="rateup"><a title="我喜欢" onclick="rup()">+</a></span><span class="ratedown"><a title="我不喜欢" onclick="rdown()">–</a></span><span class="cancel"><a title="取消我的投票" onclick="rcancel()">x</a></span></div>
<!--↑评分模块↑-->
<!--↓删除倒计时↓-->
<div id="allcontent">
<div style="color: red;">This page will be eligible for deletion in:</div>
<div style="font-size: 12pt; font-weight: bold;">1 d 0 h 0 m 0 s</div>
</div>
<!--↑删除倒计时↑-->
<!--↓空页面提示↓-->
<div id="_rmpage">
<div class="warning">
<div style="text-align: center;">
<p><span style="font-size:x-large;"><span style="color: red"><strong>一个不存在的页面!</strong></span></span></p>
<hr>
<p>哦,尊敬的先生或女士,这个地方似乎已经被相关人员处理掉了。</p>
<p>或者——请仔细检查<strong>链接</strong>,它是<em>你想要的页面吗</em>?</p>
</div>
</div>
<br>
<div style="text-align: center;">
<blockquote>
<p>请仔细阅读<a href="https://goc-wiki-cn.wikidot.com/rules">网站站规</a>;如果你想创作一篇原创文档,也请仔细阅读<a href="https://goc-wiki-cn.wikidot.com/format-help">写作指导</a>;你想要了解GOC,那么最好去查看<a href="https://goc-wiki-cn.wikidot.com/supplementary1">物理部门外勤手册</a>和<a href="https://goc-wiki-cn.wikidot.com/about">关于GOC</a></p>
<p>想好了吗?那么请离开这里,或者<strong><a onclick="newPage()">点击此处创建页面</a></strong></p>
</blockquote>
<h1 id="toc0"><span><span style="color: blue" id="lastheader">人类至上,常态至上</span></span></h1>
</div>
</div>
<!--↑空页面提示↑-->
<script type="text/javascript">
var body = document.getElementsByTagName("body")[0]
var rateNum = document.getElementById("rate-num")
var rateStorage = localStorage.getItem("rate")
var allcontent = document.getElementById("allcontent")
allcontent.remove()
var _rmpage = document.getElementById("_rmpage")
_rmpage.remove()
if (rateStorage == "up") rateNum.innerHTML = "0"
else if (rateStorage == "down") {
// ↓删除页面中除第一个元素外的全部元素↓
for (let i = 1;i < body.children.length;i++) body.children[i].remove()
// ↑在 Wikidot 的 HTML 模块中,head 标签内的元素会被放入 body 标签,因此为了避免 CSS 样式表被删除,必须从第二个元素开始删除↑
body.append(_rmpage)
}
else if (rateStorage == null) rateNum.innerHTML = "-1"
var acti
function rup() {
rateStorage = localStorage.getItem("rate")
if (rateStorage == null) {
localStorage.setItem("rate", "up")
rateNum.innerHTML = 0
}
else alert("你已经参与过此页面的评分了!")
}
function rdown() {
rateStorage = localStorage.getItem("rate")
if (rateStorage == null) {
localStorage.setItem("rate", "down")
rateNum.innerHTML = -2
body.append(allcontent)
ac()
}
else alert("你已经参与过此页面的评分了!")
}
function rcancel() {
rateStorage = localStorage.getItem("rate")
if (rateStorage != null) {
if (rateStorage == "down") {
// ↓结束定时器↓
clearInterval(acti)
// ↑结束定时器↑
allcontent.remove()
}
localStorage.removeItem("rate")
rateNum.innerHTML = -1
}
}
function ac() {
// ↓将删除倒计时的剩余时间拆分为字符串数组↓
let act = allcontent.lastElementChild.innerHTML.split(" ")
// ↑通过数组类型将更容易编辑↑
// ↓设定倒计时显示的时间↓
act[0] = 1
act[2] = 0
act[4] = 0
act[6] = 0
// ↑设定倒计时显示的时间↑
// ↓将字符串数组重新合并为完整的字符串↓
allcontent.lastElementChild.innerHTML = act.join(" ")
// ↑将字符串数组重新合并为完整的字符串↑
// ↓设置定时器并绑定在 acti 变量中↓
acti = setInterval(function() {
act = allcontent.lastElementChild.innerHTML.split(" ")
if (parseInt(act[6]) != 0) act[6] = parseInt(act[6]) - 10 >= 0 ? parseInt(act[6]) - 10 : parseInt(act[6]) - 9
else if (parseInt(act[4]) != 0) {
act[4] = parseInt(act[4]) - 10 >= 0 ? parseInt(act[4]) - 10 : parseInt(act[4]) - 9
act[6] = 59
} else if (parseInt(act[2]) != 0) {
act[2] = parseInt(act[2]) - 1
act[4] = 59
act[6] = 59
} else if (parseInt(act[0]) != 0) {
act[0] = parseInt(act[0]) - 1
act[2] = 23
act[4] = 59
act[6] = 59
} else clearInterval(acti)
if (act[0] == 0 && act[2] == 0 && act[4] == 0 && act[6] == 0) {
for (let i = 1;i < body.children.length;i++) body.children[i].remove()
body.append(_rmpage)
// ↓结束定时器↓
clearInterval(acti)
// ↑结束定时器↑
}
else allcontent.lastElementChild.innerHTML = act.join(" ")
}, 0)
// ↑定时器每0毫秒执行一次,但实际运行中最低为1毫秒↑
}
function newPage() {
localStorage.removeItem("rate")
location.reload()
}
</script>
</body>
[[/html]]
代码相较前文的内容,看上去似乎复杂了不少,但实际上只有五个新东西:
- children: 获取元素内的子元素。其中:
- children.length: 获取元素内的子元素的数量。
- children[]: 获取元素内的第 N 个子元素(从0开始为第一个,1为第二个,以此类推)。
- split(): 根据括号中内容,将字符串拆分为字符串数组,如:
- split("-"): "1-2-3-4-5" -> [1, 2, 3, 4, 5];
- split("23"): "12345" -> [1, 45];
- 此处为根据【空格】拆分字符串。
- join: 根据括号中内容,将字符串数组合并为字符串,如:
- join("-"): [1, 2, 3, 4, 5] -> "1-2-3-4-5";
- join("23"): [1, 45] -> "12345";
- 此处为根据【空格】合并数组。
- setInterval(): 【定时器】,间隔特定时间执行代码。在此处:
- function () {}: 需要执行的代码;
- 0: 间隔时间,以毫米为单位,最低为1毫秒(设为0同样间隔1毫米);
- clearInterval(): 结束定时器,在括号中选择。此处结束了变量【acti】的定时器。
如果还是觉得复杂怎么办呢?那就直接两眼一黑 Ctrl C V 没关系,因为……
接下来我将演示如何从头开始制作一个伪评分模块。
[[html]]
<head>
<link href="https://goc-sandbox-cn.wikidot.com/admin:themes/code/1" rel="stylesheet" type="text/css" />
</head>
<body>
<div class="page-rate-widget-box">
<span class="rate-points">评分: <span id="rate-num">0</span></span>
<span class="rateup"><a title="我喜欢" onclick="rup()">+</a></span>
<span class="ratedown"><a title="我不喜欢" onclick="rdown()">–</a></span>
<span class="cancel"><a title="取消我的投票" onclick="rcancel()">x</a></span>
</div>
</body>
[[/html]]
上面就是显示一个评分模块所需要的代码,其中的 title 属性的作用是,设定鼠标悬浮在其上时显示的提示文本。
其它部分没什么可讲的,只能死记硬背,因为 Wikidot 是这么写的2,所以我们也只能这么写。
当然了,只能远观而不能亵玩,因为我们还没有为其绑定点击事件。
接下来我们为其绑定点击事件。
[[html]]
<head>
<link href="https://goc-sandbox-cn.wikidot.com/admin:themes/code/1" rel="stylesheet" type="text/css" />
</head>
<body>
<div class="page-rate-widget-box">
<span class="rate-points">评分: <span id="rate-num">0</span></span>
<span class="rateup"><a title="我喜欢" onclick="rup()">+</a></span>
<span class="ratedown"><a title="我不喜欢" onclick="rdown()">–</a></span>
<span class="cancel"><a title="取消我的投票" onclick="rcancel()">x</a></span>
</div>
<script type="text/javascript">
var rateNum = document.getElementById("rate-num")
function rup() {
if (rateNum.innerHTML == "0") rateNum.innerHTML = 1
else alert("您已经参与过此页面的评分!")
}
function rdown() {
if (rateNum.innerHTML == "0") rateNum.innerHTML = -1
else alert("您已经参与过此页面的评分!")
}
function rcancel() {
if (rateNum.innerHTML == "0") alert("您尚未参与此页面的评分!")
else rateNum.innerHTML = 0
}
</script>
</body>
[[/html]]
一般来说,大家普遍习惯将评分模块置于右侧,那么该怎样修改呢?
[[html]]
<head>
<link href="https://goc-sandbox-cn.wikidot.com/admin:themes/code/1" rel="stylesheet" type="text/css" />
</head>
<body>
<div class="page-rate-widget-box" style="float: right;">
<span class="rate-points">评分: <span id="rate-num">0</span></span>
<span class="rateup"><a title="我喜欢" onclick="rup()">+</a></span>
<span class="ratedown"><a title="我不喜欢" onclick="rdown()">–</a></span>
<span class="cancel"><a title="取消我的投票" onclick="rcancel()">x</a></span>
</div>
<script type="text/javascript">
var rateNum = document.getElementById("rate-num")
function rup() {
if (rateNum.innerHTML == "0") rateNum.innerHTML = 1
else alert("您已经参与过此页面的评分!")
}
function rdown() {
if (rateNum.innerHTML == "0") rateNum.innerHTML = -1
else alert("您已经参与过此页面的评分!")
}
function rcancel() {
if (rateNum.innerHTML == "0") alert("您尚未参与此页面的评分!")
else rateNum.innerHTML = 0
}
</script>
</body>
[[/html]]
其中:
- style: 【行内/内联样式】属性,用于修改元素的 CSS 样式,其优先级高于内嵌式与链入式,为最高优先级。
- float: 【浮动】属性,用于修改元素的浮动方向,默认为 none ,也即无浮动。此处设置为【向右浮动】。
现在的伪评分模块会在每次刷新后重置,那么能不能记住读者的评分呢?当然可以,只需要运用前文提到的 LocalStorage 就好。
[[html]]
<head>
<link href="https://goc-sandbox-cn.wikidot.com/admin:themes/code/1" rel="stylesheet" type="text/css" />
</head>
<body>
<div class="page-rate-widget-box" style="float: right;">
<span class="rate-points">评分: <span id="rate-num">0</span></span>
<span class="rateup"><a title="我喜欢" onclick="rup()">+</a></span>
<span class="ratedown"><a title="我不喜欢" onclick="rdown()">–</a></span>
<span class="cancel"><a title="取消我的投票" onclick="rcancel()">x</a></span>
</div>
<script type="text/javascript">
var rateNum = document.getElementById("rate-num")
var rateStorage = localStorage.getItem("rateStorage")
if (rateStorage == "up") rateNum.innerHTML = 1
else if (rateStorage == "down") rateNum.innerHTML = -1
function rup() {
rateStorage = localStorage.getItem("rateStorage")
if (rateStorage == null) {
localStorage.setItem("rateStorage", "up")
rateNum.innerHTML = 1
}
else alert("您已经参与过此页面的评分!")
}
function rdown() {
rateStorage = localStorage.getItem("rateStorage")
if (rateStorage == null) {
localStorage.setItem("rateStorage", "down")
rateNum.innerHTML = -1
}
else alert("您已经参与过此页面的评分!")
}
function rcancel() {
rateStorage = localStorage.getItem("rateStorage")
if (rateStorage == null) alert("您尚未参与此页面的评分!")
else {
localStorage.removeItem("rateStorage")
rateNum.innerHTML = 0
}
}
</script>
</body>
[[/html]]
其后的内容就可以由各位自行发挥了,相信大家可以搞出很不错的创意来。
如果不做 Meta 页面,互动代码就毫无作用了吗?当然不会,接下来我将演示一个可以用在很多地方的动态的类聊天记录内容:
在页面中使用如下代码以加入此功能(不完整版):
[[include :goc-sandbox-cn:chat-box
| chatBoxName=标题
| chatHeadA=http://scp-wiki.wikidot.com/local--files/art:goc-logos/High-Command.png
| chatNameA=第一条消息的发送者的昵称
| chatTextA=出现的第一条消息的文字内容。
| ButtonA=选项 A
| ButtonB=选项 B
| buttonHeadA=http://goc-sandbox-cn.wikidot.com/local--files/code-help/xiu.png
| buttonNameA=选项 A 中所使用的昵称
| buttonTextA=选择选项 A 后发送的消息中的文字内容
| chatTimeA=1000
| chatHeadB=http://scp-wiki.wikidot.com/local--files/art:goc-logos/High-Command.png
| chatNameB=选项 A 中对方所使用的昵称
| chatTextB=选择选项 A 后对方回复的消息中的文字内容
| buttonHeadB=http://goc-sandbox-cn.wikidot.com/local--files/code-help/xiu.png
| buttonNameB=选项 B 中所使用的昵称
| buttonTextB=选择选项 B 后发送的消息中的文字内容
| chatTimeB=1000
| chatHeadC=http://scp-wiki.wikidot.com/local--files/art:goc-logos/High-Command.png
| chatNameC=选项 B 中对方所使用的昵称
| chatTextC=选择选项 B 后对方回复的消息中的文字内容
]]
其中:
- chatBoxName: 设置标题。
- chatHeadA: 设置第一条消息的发送者的头像的 URL 。
- chatNameA: 设置第一条消息的发送者的昵称。
- chatTextA: 设置出现的第一条消息的文字内容。
- ButtonA: 设置选项 A 的文本。
- ButtonB: 设置选项 B 的文本。
- buttonHeadA: 设置选项 A 中所使用的头像的 URL 。
- buttonNameA: 设置选项 A 中所使用的昵称。
- buttonTextA: 设置选择选项 A 后发送的消息中的文字内容。
- chatTimeA: 设置选择选项 A 后对方回复前的等待时间。
- chatHeadB: 设置选项 A 中对方所使用的头像的 URL 。
- chatNameB: 设置选项 A 中对方所使用的昵称
- chatTextB: 设置选择选项 A 后对方回复的消息中的文字内容
- buttonHeadB: 设置选项 B 中所使用的头像的 URL 。
- buttonNameB: 设置选项 B 中所使用的昵称。
- buttonTextB: 设置选择选项 B 后发送的消息中的文字内容。
- chatTimeB: 设置选择选项 B 后对方回复前的等待时间。
- chatHeadC: 设置选项 B 中对方所使用的头像的 URL 。
- chatNameC: 设置选项 B 中对方所使用的昵称
- chatTextC: 设置选择选项 B 后对方回复的消息中的文字内容
如你所见的,该组件 必须且最多 设置两个选项,同时 必须且最多 为每个选项设置一条发出消息和一条回复消息。事实上,受制于 Wikidot 的特性,我只能将组件做到这样的程度。
如果想要一个高度自定义的版本,不妨看看该功能是如何实现的。
[[html]]
<head>
<link href="https://goc-sandbox-cn.wikidot.com/admin:themes/code/1" rel="stylesheet" type="text/css" />
<style type="text/css">
#chatBox {
position: relative;
margin: 1rem auto;
width: 80%;
height: 540px;
border: solid 1px rgb(164, 194, 236);
border-radius: 8px;
box-shadow: 0 0 3px 0 rgb(12, 12, 12);
}
#chatTop {
display: flex;
justify-content: space-between;
position: absolute;
z-index: 1;
width: 100%;
height: 2rem;
border-radius: 4px 4px 0 0;
box-shadow: 0 1px 1px 0 rgb(12, 12, 12, 0.5);
background: linear-gradient(rgb(164, 194, 236), rgb(92, 149, 230));
}
#chatTop span {
display: block;
margin: auto 8px;
font-family: 'Russo One', "Noto Sans SC", sans-serif;
font-weight: bold;
}
#chatBody {
--padding: 2rem;
padding-top: var(--padding);
padding-bottom: var(--padding);
width: 100%;
height: calc(100% - 2 * var(--padding));
border-radius: 8px;
background: rgb(236, 236, 236) url('https://scp-wiki.wdfiles.com/local--files/theme%3Agoc/goc-fade.png') center no-repeat;
overflow-y: auto;
}
.chatItem {
display: flex;
margin: 1rem 0;
transform-origin: left bottom;
transform: scale(1);
font-family: PTRootUI,"方体","PingFang SC","黑体","Heiti SC","Microsoft JhengHei UI","Microsoft JhengHei",Roboto,Noto,"Noto Sans CJK SC",sans-serif;
animation: messageInput 300ms;
}
.chatItem.right {
justify-content: end;
transform-origin: right bottom;
}
.head {
display: block;
margin: 8px;
width: 12%;
max-width: 64px;
height: 12%;
max-height: 64px;
border-radius: 6px;
background-color: rgb(252, 252, 252);
}
.chatItemBody {
display: block;
margin-top: 18px;
max-width: 55%;
}
.name {
display: block;
font-size: .8em;
}
.right .name {
text-align: right;
}
.message {
display: inline-block;
margin-top: 6px;
padding: 6px;
border-radius: 6px;
border-top-left-radius: 0;
background-color: rgb(252, 252, 252);
}
.right .message {
border-top-left-radius: 6px;
border-top-right-radius: 0;
}
.markMessage {
color: rgb(96, 96, 96);
text-align: center;
font-size: .8em;
font-family: 'Russo One', "Noto Sans SC", sans-serif;
}
#radioButton {
--border-width: 3px;
display: flex;
justify-content: space-around;
position: absolute;
bottom: 0;
width: calc(100% - 2 * var(--border-width));
border-radius: 0 0 8px 8px;
border: solid var(--border-width) rgb(252, 252, 252);
background-color: rgb(252, 252, 252);
}
#radioButton button {
width: 40%;
font-family: 'Russo One', "Noto Sans SC", sans-serif !important;
font-weight: bold !important;
}
@keyframes messageInput {
0% {transform: scale(0);}
100% {transform: scale(1);}
}
</style>
</head>
<body>
<div id="chatBox">
<div id="chatTop">
<span><</span>
<span>聊天室(3741)</span>
<span>≡</span>
</div>
<div id="chatBody">
<div class="chatItem">
<img src="http://scp-wiki.wikidot.com/local--files/art:goc-logos/High-Command.png" class="head" />
<div class="chatItemBody">
<span class="name">D.C.Al fine</span>
<span class="message">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quam distinctio minima ratione delectus aperiam sunt earum voluptas. Rerum, cumque nesciunt. Ad a voluptate eius nam, iusto fugiat reprehenderit voluptates iure!</span>
</div>
</div>
<div id="radioButton">
<button onclick="a()">你在说啥?</button>
<button onclick="b()">はい!</button>
</div>
</div>
</div>
<script type="text/javascript">
var chatBody = document.getElementById("chatBody")
var radioButton = document.getElementById("radioButton")
function a() {
radioButton.remove()
let chatItem = document.createElement("div")
chatItem.classList.add("chatItem", "right")
let chatItemBody = document.createElement("div")
chatItemBody.classList.add("chatItemBody")
let headImage = document.createElement("img")
headImage.classList.add("head")
headImage.src = "http://goc-sandbox-cn.wikidot.com/local--files/code-help/xiu.png"
let name = document.createElement("span")
name.classList.add("name")
name.innerHTML = "荷修"
chatItemBody.append(name)
let message = document.createElement("span")
message.classList.add("message")
message.innerHTML = "🧐秘书长,我不知道您在说什么🥺"
chatItemBody.append(message)
chatItem.append(chatItemBody)
chatItem.append(headImage)
chatBody.append(chatItem)
setTimeout(function() {
let markMessage = document.createElement("p")
markMessage.classList.add("markMessage")
markMessage.innerHTML = "您已被 D.C.Al fine 踢出群聊"
chatBody.append(markMessage)
},3000)
}
function b() {
radioButton.remove()
let chatItem = document.createElement("div")
chatItem.classList.add("chatItem", "right")
let chatItemBody = document.createElement("div")
chatItemBody.classList.add("chatItemBody")
let headImage = document.createElement("img")
headImage.classList.add("head")
headImage.src = "http://goc-sandbox-cn.wikidot.com/local--files/code-help/xiu.png"
let name = document.createElement("span")
name.classList.add("name")
name.innerHTML = "荷修"
chatItemBody.append(name)
let message = document.createElement("span")
message.classList.add("message")
message.innerHTML = "👊🇺🇳🔥"
chatItemBody.append(message)
chatItem.append(chatItemBody)
chatItem.append(headImage)
chatBody.append(chatItem)
setTimeout(c, 1000)
}
function c() {
let chatItem = document.createElement("div")
chatItem.classList.add("chatItem")
let chatItemBody = document.createElement("div")
chatItemBody.classList.add("chatItemBody")
let headImage = document.createElement("img")
headImage.classList.add("head")
headImage.src = "http://scp-wiki.wikidot.com/local--files/art:goc-logos/High-Command.png"
let name = document.createElement("span")
name.classList.add("name")
name.innerHTML = "D.C.Al fine"
chatItemBody.append(name)
let message = document.createElement("span")
message.classList.add("message")
message.innerHTML = "👊🇺🇳🔥"
chatItemBody.append(message)
chatItem.append(headImage)
chatItem.append(chatItemBody)
chatBody.append(chatItem)
setTimeout(d, 700)
}
function d() {
var scsi = setInterval(function(){
chatBody.scrollTop = chatBody.scrollHeight
} ,0)
let heads = new Array
let names = new Array
heads.push("http://scp-wiki.wikidot.com/local--files/art:goc-logos/International-Paracriminal-Court.png")
names.push("国际超刑事法院")
heads.push("http://scp-wiki.wikidot.com/local--files/art:goc-logos/PHYSICS.png")
names.push("物理部门")
heads.push("https://scp-wiki.wikidot.com/local--files/art:goc-logos/PSYCHE.png")
names.push("心智部门")
heads.push("https://scp-wiki.wikidot.com/local--files/art:goc-logos/PTOLEMY.png")
names.push("神秘部门")
heads.push("https://scp-wiki.wikidot.com/local--files/art:goc-logos/Bavarian-Illuminati.png")
names.push("巴伐利亚光明会")
heads.push("https://scp-wiki.wikidot.com/local--files/art:goc-logos/Five-Elements-Association.png")
names.push("五行结社")
heads.push("https://scp-wiki.wikidot.com/local--files/art:goc-logos/Goldbaker-Reinz.png")
names.push("金贝格-莱兹")
heads.push("https://scp-wiki.wikidot.com/local--files/art:goc-logos/Gormogons.png")
names.push("哥尔摩根古圣教团")
heads.push("https://scp-wiki.wikidot.com/local--files/art:goc-logos/ICSUT.png")
names.push("国际统合奇术研究中心")
heads.push("https://scp-wiki.wikidot.com/local--files/art:goc-logos/Knights-Templar.png")
names.push("联合撒旦教会,科学派")
heads.push("https://scp-wiki.wikidot.com/local--files/art:goc-logos/World-Parahealth-Organization.png")
names.push("世界超卫生组织")
heads.push("https://scp-wiki.wikidot.com/local--files/art:goc-logos/Universalist-Order-of-the-Aesir.png")
names.push("阿萨神族之普世教团")
heads.push("https://scp-wiki.wikidot.com/local--files/art:goc-logos/Aetheric-Engineers.png")
names.push("以太工程师")
heads.push("https://scp-wiki.wikidot.com/local--files/art:goc-logos/Alchemists-Guild.png")
names.push("炼金术师公会")
heads.push("https://scp-wiki.wikidot.com/local--files/art:goc-logos/Australian-Abnormal-Intelligence-Service.png")
names.push("澳大利亚异常情报局")
heads.push("https://scp-wiki.wikidot.com/local--files/art:goc-logos/British-Occult-Service.png")
names.push("英国超自然部队")
heads.push("https://scp-wiki.wikidot.com/local--files/art:goc-logos/Church-of-California-Rolls.png")
names.push("加州寿司卷教会")
heads.push("https://scp-wiki.wikidot.com/local--files/art:goc-logos/Coca-Cola.png")
names.push("可口可乐公司")
heads.push("https://scp-wiki.wikidot.com/local--files/art:goc-logos/Electorate-of-Demomancers.png")
names.push("恶魔术士选民团")
heads.push("https://scp-wiki.wikidot.com/local--files/art:goc-logos/Freemasons.png")
names.push("共济会")
heads.push("https://scp-wiki.wikidot.com/local--files/art:goc-logos/Iron-Monks.png")
names.push("铁僧团")
heads.push("https://scp-wiki.wikidot.com/local--files/art:goc-logos/Knights-Hospitaller.png")
names.push("马耳他主权军事骑士团(医院骑士团)")
heads.push("https://scp-wiki.wikidot.com/local--files/art:goc-logos/Majestic-12.png")
names.push("雄伟12人")
heads.push("https://scp-wiki.wikidot.com/local--files/art:goc-logos/Nyau.png")
names.push("尼奥密教骑士会")
heads.push("https://scp-wiki.wikidot.com/local--files/art:goc-logos/Order-of-the-Dragon.png")
names.push("龙骑士团")
heads.push("https://scp-wiki.wikidot.com/local--files/art:goc-logos/Parapsychic-Consultants.png")
names.push("超灵学咨询处")
heads.push("https://scp-wiki.wikidot.com/local--files/art:goc-logos/Pentagram.png")
names.push("五芒星")
heads.push("https://scp-wiki.wikidot.com/local--files/art:goc-logos/Pepsi.png")
names.push("百事公司")
heads.push("https://scp-wiki.wikidot.com/local--files/art:goc-logos/Pr-Ankh-n-Djehuti.png")
names.push("Pr-Ankh-n-Djehuti(托特之图书馆)")
heads.push("https://scp-wiki.wikidot.com/local--files/art:goc-logos/SAPPHIRE.png")
names.push("蓝宝石")
heads.push("https://scp-wiki.wikidot.com/local--files/art:goc-logos/Sons-of-Phomet.png")
names.push("弗灭之子")
heads.push("https://scp-wiki.wikidot.com/local--files/art:goc-logos/UNESCO.png")
names.push("联合国教科文组织")
heads.push("https://scp-wiki.wikidot.com/local--files/art:goc-logos/Disney.png")
names.push("华特迪士尼公司")
heads.push("https://scp-wiki.wikidot.com/local--files/art:goc-logos/Zenith-Group.png")
names.push("天顶集团")
heads.push("https://scp-wiki.wikidot.com/local--files/art:goc-logos/Fraternitas-Saturni.png")
names.push("土星兄弟会")
heads.push("https://scp-wiki.wikidot.com/local--files/art:goc-logos/International-Association-for-Psychotronic-Research.png")
names.push("国际超心理学研究协会")
heads.push("https://scp-wiki.wikidot.com/local--files/art:goc-logos/International-Astrological-Union.png")
names.push("国际占星学联合会")
heads.push("https://scp-wiki.wikidot.com/local--files/art:goc-logos/Intiq-Churinkuna.png")
names.push("Intiq Churinkuna(印蒂之子)")
heads.push("https://scp-wiki.wikidot.com/local--files/art:goc-logos/Order-of-Assassins.png")
names.push("حشاشين(阿萨辛刺客团)")
heads.push("https://scp-wiki.wikidot.com/local--files/art:goc-logos/Order-of-the-Rose-Cross.png")
names.push("玫瑰十字会")
heads.push("https://scp-wiki.wikidot.com/local--files/art:goc-logos/Poro-Society.png")
names.push("波罗社")
heads.push("https://scp-wiki.wikidot.com/local--files/art:goc-logos/Red-Lanterns.png")
names.push("红灯照")
heads.push("https://scp-wiki.wikidot.com/local--files/art:goc-logos/Sevite-Lwa.png")
names.push("洛阿神仆会")
heads.push("https://scp-wiki.wikidot.com/local--files/art:goc-logos/United-Order-of-Druids.png")
names.push("德鲁伊联合会")
heads.push("https://scp-wiki.wikidot.com/local--files/art:goc-logos/Weyward-Sisters.png")
names.push("韦沃德姐妹会")
heads.push("https://scp-wiki.wikidot.com/local--files/art:goc-logos/Yellow-Shamans.png")
names.push("黄萨满")
let i = 0
let inte = setInterval(function(){
let chatItem = document.createElement("div")
chatItem.classList.add("chatItem")
let chatItemBody = document.createElement("div")
chatItemBody.classList.add("chatItemBody")
let headImage = document.createElement("img")
headImage.classList.add("head")
headImage.src = heads[i]
let name = document.createElement("span")
name.classList.add("name")
name.innerHTML = names[i++]
chatItemBody.append(name)
let message = document.createElement("span")
message.classList.add("message")
message.innerHTML = "👊🇺🇳🔥"
chatItemBody.append(message)
chatItem.append(headImage)
chatItem.append(chatItemBody)
chatBody.append(chatItem)
if (i >= heads.length || i >= names.length) {
clearInterval(inte)
setTimeout(function() {
clearInterval(scsi)
}, 1000)
}
}, 500)
}
</script>
</body>
[[/html]]
可悲的事实是:它太长了,我没办法详细地解释每一条的作用,除非大家愿意把网页三件套3都粗略学一遍。
值得一提的是,我很乐意像前文的示例一样,从头开始复刻一个简单的版本。
先从聊天室的嵌套结构开始吧:
[[html]]
<head>
<link href="https://goc-sandbox-cn.wikidot.com/admin:themes/code/1" rel="stylesheet" type="text/css" />
</head>
<body>
<div class="chatBox">
<div class="chatTop">此处是类聊天记录的顶栏</div>
<div class="chatBody">
此处是类聊天记录的主体
<div class="radioButton">此处是类聊天记录的选项</div>
</div>
</div>
</body>
[[/html]]
这是类聊天记录最基本的嵌套结构,但目前还没有为其添加样式,因此看不出任何效果:
现在我们开始编写样式表吧,第一步就是为 div 添加背景与边框:
[[html]]
<head>
<link href="https://goc-sandbox-cn.wikidot.com/admin:themes/code/1" rel="stylesheet" type="text/css" />
<style type="text/css">
.chatBox {
border: solid 1px rgb(164, 194, 236);
}
.chatTop {
background: linear-gradient(rgb(164, 194, 236), rgb(92, 149, 230));
}
.chatBody {
background: rgb(236, 236, 236) url('https://scp-wiki.wdfiles.com/local--files/theme%3Agoc/goc-fade.png') center no-repeat;
}
.radioButton {
border: solid 3px rgb(252, 252, 252);
background: rgb(252, 252, 252);
}
</style>
</head>
<body>
<div class="chatBox">
<div class="chatTop">此处是类聊天记录的顶栏</div>
<div class="chatBody">
此处是类聊天记录的主体
<div class="radioButton">此处是类聊天记录的选项</div>
</div>
</div>
</body>
[[/html]]
其中:
- .chatBox {}: 【类】选择器,同前文的标签选择器、id 选择器类似。后文的【.XXX】同。
- border: 设置边框样式,其中:
- solid: 边框类型,此处为【实线】,此外还有:
- double: 双实线。
- dotted: 点状线。
- dashed: 虚线。
- groove: 槽状。
- ridge: 垄状。
- outset: 外凸。
- inset: 内凹。
- 1px: 边框宽度,此处为1像素。
- rgb(): 边框颜色,此处使用 RGB 色号,此外还可以使用:
- red: 直接输入颜色名称。
- #XXXXXX: 16进制色号。
- rgba(): RGBA 色号,与 RGB 色号的区别在于,其多出一位 Alpha 值,即透明度。
- solid: 边框类型,此处为【实线】,此外还有:
- background: 设置背景样式。
- linear-gradient: 设置渐变色,默认从上至下,前为起始色,后为结束色。
- rgb(): 设置背景颜色,具体同边框颜色。
- url(): 设置背景图片的 URL 。
- center: 设置背景居中显示。
- no-repeat: 设置背景不重复显示。
现在它看上去很扁,这是由于我们还没有设定其宽高,现在我们设置一下看看:
[[html]]
<head>
<link href="https://goc-sandbox-cn.wikidot.com/admin:themes/code/1" rel="stylesheet" type="text/css" />
<style type="text/css">
.chatBox {
width: 80%;
height: 540px;
border: solid 1px rgb(164, 194, 236);
}
.chatTop {
height: 2rem;
background: linear-gradient(rgb(164, 194, 236), rgb(92, 149, 230));
}
.chatBody {
height: 100%;
background: rgb(236, 236, 236) url('https://scp-wiki.wdfiles.com/local--files/theme%3Agoc/goc-fade.png') center no-repeat;
}
.radioButton {
border: solid 3px rgb(252, 252, 252);
background: rgb(252, 252, 252);
}
</style>
</head>
<body>
<div class="chatBox">
<div class="chatTop">此处是类聊天记录的顶栏</div>
<div class="chatBody">
此处是类聊天记录的主体
<div class="radioButton">此处是类聊天记录的选项</div>
</div>
</div>
</body>
[[/html]]
其中:
- width: 宽度。
- height: 高度。
值得一提的是,除最外层的元素外,当使用百分比作为宽高的数字时,它将以它的父元素为100%取值。像此处的 .chatBox 的宽度所设置的80%就是指父元素宽度的80%,而 .chatBody 的高度所设置的100%则是指父元素高度的100%。
看上去还是很奇怪……这是由于我们没有正确设置 边距 和 定位 ,那么现在设置一下吧:
[[html]]
<head>
<link href="https://goc-sandbox-cn.wikidot.com/admin:themes/code/1" rel="stylesheet" type="text/css" />
<style type="text/css">
.chatBox {
position: relative;
margin: 1rem auto;
width: 80%;
height: 540px;
border: solid 1px rgb(164, 194, 236);
}
.chatTop {
position: absolute;
z-index: 1;
width: 100%;
height: 2rem;
background: linear-gradient(rgb(164, 194, 236), rgb(92, 149, 230));
}
.chatBody {
--padding: 2rem;
padding-top: var(--padding);
padding-bottom: var(--padding);
height: calc(100% - 2 * var(--padding));
background: rgb(236, 236, 236) url('https://scp-wiki.wdfiles.com/local--files/theme%3Agoc/goc-fade.png') center no-repeat;
}
.radioButton {
--border-width: 3px;
position: absolute;
bottom: 0;
width: calc(100% - 2 * var(--border-width));
border: solid var(--border-width) rgb(252, 252, 252);
background: rgb(252, 252, 252);
}
</style>
</head>
<body>
<div class="chatBox">
<div class="chatTop">此处是类聊天记录的顶栏</div>
<div class="chatBody">
此处是类聊天记录的主体
<div class="radioButton">此处是类聊天记录的选项</div>
</div>
</div>
</body>
[[/html]]
其中:
- position: 设置元素的定位方式,包括:
- static: 默认定位,即正常根据文档布局流进行定位。
- relative: 相对定位,即在根据文档布局流定位后,相对自身位置进行偏移。
- absolute: 绝对定位,即脱离文档布局流,根据其最近的非默认定位的祖先元素(如果有)进行定位,若无则根据页面进行定位。
- 元素脱离原有的文档布局流后,宽度将无法自动填满父元素,因此后面的 .radioButton 需要手动设置宽度。
- fixed: 固定定位,即脱离文档布局流,根据浏览器进行定位。
- margin: 设置元素的外边距,即元素与其它元素的间距。
- 当被设置一个值时: 四周外边距。
- 当被设置两个值时: 垂直外边距 水平外边距。
- 当被设置三个值时: 上方外边距 水平外边距 下方外边距。
- 当被设置四个值时: 上方外边距 右侧外边距 下方外边距 左侧外边距。
- auto: 自动,即视情况自动填充。此处将 .chatBox 的水平外边距被设置为 auto 能够使其水平居中。
- z-index: 设置元素的层级,仅当元素的定位方式为 absolute 或 fixed 时生效。
- 一般情况下,在文档流中越靠后的元素,其层级越高,因而此处为了让 .chatTop 能够置于其后的 .chatBody 之上,设置了 z-index 为1(默认0,越大则越高)。
- --padding: 变量。此处为了提高代码的可读性设置了变量,后面的 --border-width 同。
- CSS 中的变量只能在自身或其子元素中生效。
- 设置方法为:--XXX: N; 。
- 使用方法为:var(--XXX) 。
- padding-top: 顶部内边距,即其顶部边框与其子元素的间距。
- 值得注意的是,padding 的宽度会与元素的宽高相加,使得最后的宽高等于正常宽高 + padding 的宽度,处于这个原因,后面的 height 被重新设置。
- padding-bottom: 底部外边距,其余同上。
- calc(): 算术表达式,用于执行一些计算。此处用于计算 .chatBody 减去其内边距后的高度。
- bottom: 底部偏移。与 margin 不同的是,使用 margin 会增加元素所占用的空间,偏移则不会。类似的还有 left 、right 、top 。
这下看上去正常多了,接下来就开始设置圆角和阴影,让其更加立体吧:
[[html]]
<head>
<link href="https://goc-sandbox-cn.wikidot.com/admin:themes/code/1" rel="stylesheet" type="text/css" />
<style type="text/css">
.chatBox {
position: relative;
margin: 1rem auto;
width: 80%;
height: 540px;
border: solid 1px rgb(164, 194, 236);
border-radius: 8px;
box-shadow: 0 0 3px 0 rgb(12, 12, 12);
}
.chatTop {
position: absolute;
z-index: 1;
width: 100%;
height: 2rem;
border-radius: 4px 4px 0 0;
box-shadow: 0 1px 1px 0 rgb(12, 12, 12, 0.5);
background: linear-gradient(rgb(164, 194, 236), rgb(92, 149, 230));
}
.chatBody {
--padding: 2rem;
padding-top: var(--padding);
padding-bottom: var(--padding);
height: calc(100% - 2 * var(--padding));
border-radius: 8px;
background: rgb(236, 236, 236) url('https://scp-wiki.wdfiles.com/local--files/theme%3Agoc/goc-fade.png') center no-repeat;
}
.radioButton {
--border-width: 3px;
position: absolute;
bottom: 0;
width: calc(100% - 2 * var(--border-width));
border: solid var(--border-width) rgb(252, 252, 252);
border-radius: 0 0 8px 8px;
background: rgb(252, 252, 252);
}
</style>
</head>
<body>
<div class="chatBox">
<div class="chatTop">此处是类聊天记录的顶栏</div>
<div class="chatBody">
此处是类聊天记录的主体
<div class="radioButton">此处是类聊天记录的选项</div>
</div>
</div>
</body>
[[/html]]
其中:
- border-radius: 设置圆角大小。
- 当被设置一个值时: 四角圆角大小。
- 当被设置两个值时: 左上与右下圆角大小 右上与左下圆角大小。
- 当被设置四个值时: 左上圆角大小 右上圆角大小 右下圆角大小 左下圆角大小。
- 此外还可以设置 椭圆 圆角,但这里不做延申。
- box-shadow: 设置盒子阴影,其中:
- 第一个参数: 水平偏移量。
- 第二个参数: 垂直偏移量。
- 第三个参数: 阴影发散距离(即阴影终点的距离)。
- 第四个参数: 阴影扩张距离(即阴影起点扩大的距离)。
- 第五个参数: 阴影颜色。
- 第六个参数(可选): 阴影方向。
- 例如 inset ,即内嵌阴影。
现在我们开始填充它的内容,如顶栏内容、消息与选项按钮:
[[html]]
<head>
<link href="https://goc-sandbox-cn.wikidot.com/admin:themes/code/1" rel="stylesheet" type="text/css" />
<style type="text/css">
.chatBox {
position: relative;
margin: 1rem auto;
width: 80%;
height: 540px;
border: solid 1px rgb(164, 194, 236);
border-radius: 8px;
box-shadow: 0 0 3px 0 rgb(12, 12, 12);
}
.chatTop {
position: absolute;
z-index: 1;
width: 100%;
height: 2rem;
border-radius: 4px 4px 0 0;
box-shadow: 0 1px 1px 0 rgb(12, 12, 12, 0.5);
background: linear-gradient(rgb(164, 194, 236), rgb(92, 149, 230));
}
.chatBody {
--padding: 2rem;
padding-top: var(--padding);
padding-bottom: var(--padding);
height: calc(100% - 2 * var(--padding));
border-radius: 8px;
background: rgb(236, 236, 236) url('https://scp-wiki.wdfiles.com/local--files/theme%3Agoc/goc-fade.png') center no-repeat;
}
.radioButton {
--border-width: 3px;
position: absolute;
bottom: 0;
width: calc(100% - 2 * var(--border-width));
border: solid var(--border-width) rgb(252, 252, 252);
border-radius: 0 0 8px 8px;
background: rgb(252, 252, 252);
}
</style>
</head>
<body>
<div class="chatBox">
<div class="chatTop">
<span><</span>
<span>聊天室标题</span>
<span>≡</span>
</div>
<div class="chatBody">
<div class="chatItem">
<img src="http://scp-wiki.wikidot.com/local--files/art:goc-logos/High-Command.png" class="head" />
<div class="chatItemBody">
<span class="name">昵称</span>
<span class="message">消息文字。</span>
</div>
</div>
<div class="radioButton">
<button>选项A</button>
<button>选项B</button>
</div>
</div>
</div>
</body>
[[/html]]
其中:
- span: 行内文字,即插入一些文字,且不占用新的段落(而 p 则会创建新的段落)。
- img: 图片。
- src: 图片的 URL 。
有了前面的经验,我们知道这依旧是样式表的问题,所以接下来继续编写样式表:
[[html]]
<head>
<link href="https://goc-sandbox-cn.wikidot.com/admin:themes/code/1" rel="stylesheet" type="text/css" />
<style type="text/css">
.chatBox {
position: relative;
margin: 1rem auto;
width: 80%;
height: 540px;
border: solid 1px rgb(164, 194, 236);
border-radius: 8px;
box-shadow: 0 0 3px 0 rgb(12, 12, 12);
}
.chatTop {
display: flex;
justify-content: space-between;
position: absolute;
z-index: 1;
width: 100%;
height: 2rem;
border-radius: 4px 4px 0 0;
box-shadow: 0 1px 1px 0 rgb(12, 12, 12, 0.5);
background: linear-gradient(rgb(164, 194, 236), rgb(92, 149, 230));
}
.chatTop span {
display: block;
margin: auto 8px;
font-family: 'Russo One', "Noto Sans SC", sans-serif;
font-weight: bold;
}
.chatBody {
--padding: 2rem;
padding-top: var(--padding);
padding-bottom: var(--padding);
height: calc(100% - 2 * var(--padding));
border-radius: 8px;
background: rgb(236, 236, 236) url('https://scp-wiki.wdfiles.com/local--files/theme%3Agoc/goc-fade.png') center no-repeat;
}
.chatItem {
display: flex;
margin: 1rem 0;
font-family: PTRootUI,"方体","PingFang SC","黑体","Heiti SC","Microsoft JhengHei UI","Microsoft JhengHei",Roboto,Noto,"Noto Sans CJK SC",sans-serif;
}
.head {
display: block;
margin: 8px;
width: 12%;
max-width: 64px;
height: 12%;
max-height: 64px;
border-radius: 6px;
background-color: rgb(252, 252, 252);
}
.chatItemBody {
display: block;
margin-top: 18px;
max-width: 55%;
}
.name {
display: block;
font-size: .8em;
}
.message {
display: inline-block;
margin-top: 6px;
padding: 6px;
border-radius: 6px;
border-top-left-radius: 0;
background-color: rgb(252, 252, 252);
}
.radioButton {
--border-width: 3px;
display: flex;
justify-content: space-around;
position: absolute;
bottom: 0;
width: calc(100% - 2 * var(--border-width));
border: solid var(--border-width) rgb(252, 252, 252);
border-radius: 0 0 8px 8px;
background: rgb(252, 252, 252);
}
.radioButton button {
width: 40%;
font-family: 'Russo One', "Noto Sans SC", sans-serif !important;
font-weight: bold !important;
}
</style>
</head>
<body>
<div class="chatBox">
<div class="chatTop">
<span><</span>
<span>聊天室标题</span>
<span>≡</span>
</div>
<div class="chatBody">
<div class="chatItem">
<img src="http://scp-wiki.wikidot.com/local--files/art:goc-logos/High-Command.png" class="head" />
<div class="chatItemBody">
<span class="name">昵称</span>
<span class="message">消息文字。</span>
</div>
</div>
<div class="radioButton">
<button>选项A</button>
<button>选项B</button>
</div>
</div>
</div>
</body>
[[/html]]
其中:
- display: 设置元素类型,包括:
- block: 块元素,即创建一个新段落并占用特定宽高的完整空间。
- inline-block: 行内块元素,即占用特定宽高的空间,但不创建新的段落。
- inline: 行内元素,即按照正常文档流的大小分配空间,不创建新的段落。
- flex: 弹性盒子,类似块元素,但内部空间弹性分配。
- justify-content: 设置弹性盒子内子元素排列方式,仅在 flex 元素中生效,包括:
- start: 从行首位置开始自动排列。
- end: 从行尾位置开始自动排列。
- center: 从中心位置开始自动排列。
- space-between: 均匀排列,弹性分配间距,两端紧贴边框。
- space-around: 均匀排列,弹性分配间距,两端与边框为弹性间距。
- space-evenly: 均匀排列,每个元素间距相等。
- font-family: 设置文字字体。
- !important: 设置该样式属性为最大优先级。
- font-weight: 设置文字粗细。
- max-width: 设置最大宽度。
- max-height: 设置最大高度。
现在我们加入【发出的消息】和【提示消息】:
[[html]]
<head>
<link href="https://goc-sandbox-cn.wikidot.com/admin:themes/code/1" rel="stylesheet" type="text/css" />
<style type="text/css">
.chatBox {
position: relative;
margin: 1rem auto;
width: 80%;
height: 540px;
border: solid 1px rgb(164, 194, 236);
border-radius: 8px;
box-shadow: 0 0 3px 0 rgb(12, 12, 12);
}
.chatTop {
display: flex;
justify-content: space-between;
position: absolute;
z-index: 1;
width: 100%;
height: 2rem;
border-radius: 4px 4px 0 0;
box-shadow: 0 1px 1px 0 rgb(12, 12, 12, 0.5);
background: linear-gradient(rgb(164, 194, 236), rgb(92, 149, 230));
}
.chatTop span {
display: block;
margin: auto 8px;
font-family: 'Russo One', "Noto Sans SC", sans-serif;
font-weight: bold;
}
.chatBody {
--padding: 2rem;
padding-top: var(--padding);
padding-bottom: var(--padding);
height: calc(100% - 2 * var(--padding));
border-radius: 8px;
background: rgb(236, 236, 236) url('https://scp-wiki.wdfiles.com/local--files/theme%3Agoc/goc-fade.png') center no-repeat;
}
.chatItem {
display: flex;
margin: 1rem 0;
font-family: PTRootUI,"方体","PingFang SC","黑体","Heiti SC","Microsoft JhengHei UI","Microsoft JhengHei",Roboto,Noto,"Noto Sans CJK SC",sans-serif;
}
.chatItem.right {
justify-content: end;
}
.head {
display: block;
margin: 8px;
width: 12%;
max-width: 64px;
height: 12%;
max-height: 64px;
border-radius: 6px;
background-color: rgb(252, 252, 252);
}
.chatItemBody {
display: block;
margin-top: 18px;
max-width: 55%;
}
.name {
display: block;
font-size: .8em;
}
.right .name {
text-align: right;
}
.message {
display: inline-block;
margin-top: 6px;
padding: 6px;
border-radius: 6px;
border-top-left-radius: 0;
background-color: rgb(252, 252, 252);
}
.right .message {
border-top-left-radius: 6px;
border-top-right-radius: 0;
}
.markMessage {
color: rgb(96, 96, 96);
text-align: center;
font-size: .8em;
font-family: 'Russo One', "Noto Sans SC", sans-serif;
}
.radioButton {
--border-width: 3px;
display: flex;
justify-content: space-around;
position: absolute;
bottom: 0;
width: calc(100% - 2 * var(--border-width));
border: solid var(--border-width) rgb(252, 252, 252);
border-radius: 0 0 8px 8px;
background: rgb(252, 252, 252);
}
.radioButton button {
width: 40%;
font-family: 'Russo One', "Noto Sans SC", sans-serif !important;
font-weight: bold !important;
}
</style>
</head>
<body>
<div class="chatBox">
<div class="chatTop">
<span><</span>
<span>聊天室标题</span>
<span>≡</span>
</div>
<div class="chatBody">
<div class="chatItem">
<img src="http://scp-wiki.wikidot.com/local--files/art:goc-logos/High-Command.png" class="head" />
<div class="chatItemBody">
<span class="name">昵称</span>
<span class="message">消息文字。</span>
</div>
</div>
<div class="chatItem right">
<div class="chatItemBody">
<span class="name">【发送】的昵称</span>
<span class="message">【发送】的消息文字。</span>
</div>
<img src="http://scp-wiki.wikidot.com/local--files/art:goc-logos/Council-of-108.png" class="head" />
</div>
<div class="markMessage">一条提示消息</div>
<div class="radioButton">
<button>选项A</button>
<button>选项B</button>
</div>
</div>
</div>
</body>
[[/html]]
终于,我们可以开始步入正题——选项功能了:
[[html]]
<head>
<link href="https://goc-sandbox-cn.wikidot.com/admin:themes/code/1" rel="stylesheet" type="text/css" />
<style type="text/css">
.chatBox {
position: relative;
margin: 1rem auto;
width: 80%;
height: 540px;
border: solid 1px rgb(164, 194, 236);
border-radius: 8px;
box-shadow: 0 0 3px 0 rgb(12, 12, 12);
}
.chatTop {
display: flex;
justify-content: space-between;
position: absolute;
z-index: 1;
width: 100%;
height: 2rem;
border-radius: 4px 4px 0 0;
box-shadow: 0 1px 1px 0 rgb(12, 12, 12, 0.5);
background: linear-gradient(rgb(164, 194, 236), rgb(92, 149, 230));
}
.chatTop span {
display: block;
margin: auto 8px;
font-family: 'Russo One', "Noto Sans SC", sans-serif;
font-weight: bold;
}
.chatBody {
--padding: 2rem;
padding-top: var(--padding);
padding-bottom: var(--padding);
height: calc(100% - 2 * var(--padding));
border-radius: 8px;
background: rgb(236, 236, 236) url('https://scp-wiki.wdfiles.com/local--files/theme%3Agoc/goc-fade.png') center no-repeat;
}
.chatItem {
display: flex;
margin: 1rem 0;
font-family: PTRootUI,"方体","PingFang SC","黑体","Heiti SC","Microsoft JhengHei UI","Microsoft JhengHei",Roboto,Noto,"Noto Sans CJK SC",sans-serif;
}
.chatItem.right {
justify-content: end;
}
.head {
display: block;
margin: 8px;
width: 12%;
max-width: 64px;
height: 12%;
max-height: 64px;
border-radius: 6px;
background-color: rgb(252, 252, 252);
}
.chatItemBody {
display: block;
margin-top: 18px;
max-width: 55%;
}
.name {
display: block;
font-size: .8em;
}
.right .name {
text-align: right;
}
.message {
display: inline-block;
margin-top: 6px;
padding: 6px;
border-radius: 6px;
border-top-left-radius: 0;
background-color: rgb(252, 252, 252);
}
.right .message {
border-top-left-radius: 6px;
border-top-right-radius: 0;
}
.markMessage {
color: rgb(96, 96, 96);
text-align: center;
font-size: .8em;
font-family: 'Russo One', "Noto Sans SC", sans-serif;
}
.radioButton {
--border-width: 3px;
display: flex;
justify-content: space-around;
position: absolute;
bottom: 0;
width: calc(100% - 2 * var(--border-width));
border: solid var(--border-width) rgb(252, 252, 252);
border-radius: 0 0 8px 8px;
background: rgb(252, 252, 252);
}
.radioButton button {
width: 40%;
font-family: 'Russo One', "Noto Sans SC", sans-serif !important;
font-weight: bold !important;
}
</style>
</head>
<body>
<div class="chatBox">
<div class="chatTop">
<span><</span>
<span>聊天室标题</span>
<span>≡</span>
</div>
<div class="chatBody">
<div class="chatItem">
<img src="http://scp-wiki.wikidot.com/local--files/art:goc-logos/High-Command.png" class="head" />
<div class="chatItemBody">
<span class="name">昵称</span>
<span class="message">消息文字。</span>
</div>
</div>
<div class="chatItem right">
<div class="chatItemBody">
<span class="name">【发送】的昵称</span>
<span class="message">【发送】的消息文字。</span>
</div>
<img src="http://scp-wiki.wikidot.com/local--files/art:goc-logos/Council-of-108.png" class="head" />
</div>
<div class="markMessage">一条提示消息</div>
<div class="radioButton">
<button onclick="a()">选项A</button>
<button onclick="b()">选项B</button>
</div>
</div>
</div>
<script type="text/javascript">
var chatBody = document.getElementsByClassName("chatBody")[0]
var radioButton = document.getElementsByClassName("radioButton")[0]
function a() {
radioButton.remove()
let chatItem = document.createElement("div")
chatItem.classList.add("chatItem", "right")
let chatItemBody = document.createElement("div")
chatItemBody.classList.add("chatItemBody")
let headImage = document.createElement("img")
headImage.classList.add("head")
headImage.src = "http://scp-wiki.wikidot.com/local--files/art:goc-logos/Council-of-108.png"
let name = document.createElement("span")
name.classList.add("name")
name.innerHTML = "【发送】的昵称A"
chatItemBody.append(name)
let message = document.createElement("span")
message.classList.add("message")
message.innerHTML = "【发送】的消息内容A。"
chatItemBody.append(message)
chatItem.append(chatItemBody)
chatItem.append(headImage)
chatBody.append(chatItem)
setTimeout(c, 1000)
}
function c() {
let chatItem = document.createElement("div")
chatItem.classList.add("chatItem")
let chatItemBody = document.createElement("div")
chatItemBody.classList.add("chatItemBody")
let headImage = document.createElement("img")
headImage.classList.add("head")
headImage.src = "http://scp-wiki.wikidot.com/local--files/art:goc-logos/High-Command.png"
let name = document.createElement("span")
name.classList.add("name")
name.innerHTML = "昵称A"
chatItemBody.append(name)
let message = document.createElement("span")
message.classList.add("message")
message.innerHTML = "消息内容A。"
chatItemBody.append(message)
chatItem.append(headImage)
chatItem.append(chatItemBody)
chatBody.append(chatItem)
}
function b() {
radioButton.remove()
let chatItem = document.createElement("div")
chatItem.classList.add("chatItem", "right")
let chatItemBody = document.createElement("div")
chatItemBody.classList.add("chatItemBody")
let headImage = document.createElement("img")
headImage.classList.add("head")
headImage.src = "http://scp-wiki.wikidot.com/local--files/art:goc-logos/Council-of-108.png"
let name = document.createElement("span")
name.classList.add("name")
name.innerHTML = "【发送】的昵称B"
chatItemBody.append(name)
let message = document.createElement("span")
message.classList.add("message")
message.innerHTML = "【发送】的消息内容B。"
chatItemBody.append(message)
chatItem.append(chatItemBody)
chatItem.append(headImage)
chatBody.append(chatItem)
setTimeout(d, 1000)
}
function d() {
let chatItem = document.createElement("div")
chatItem.classList.add("chatItem")
let chatItemBody = document.createElement("div")
chatItemBody.classList.add("chatItemBody")
let headImage = document.createElement("img")
headImage.classList.add("head")
headImage.src = "http://scp-wiki.wikidot.com/local--files/art:goc-logos/High-Command.png"
let name = document.createElement("span")
name.classList.add("name")
name.innerHTML = "昵称B"
chatItemBody.append(name)
let message = document.createElement("span")
message.classList.add("message")
message.innerHTML = "消息内容B。"
chatItemBody.append(message)
chatItem.append(headImage)
chatItem.append(chatItemBody)
chatBody.append(chatItem)
}
</script>
</body>
[[/html]]
看着很长,但其实大部分内容都在前文(按钮选项部分)中涉及过,此处仅多了三个新东西:
- .classList: 类名列表。
- .classList.add: 向类名列表中添加(新的类名)。
- .src: URL 。
- setTimeout: 定时器。与 setInterval 不同的是,其作用为等待特定时间后执行。
至于多条消息、多级对话,也都分别在前文中涉及过,其大体原理是一致的,感兴趣的话可以自行尝试,此处不做演示。
最后,让我们为消息设置一个弹出动画:
[[html]]
<head>
<link href="https://goc-sandbox-cn.wikidot.com/admin:themes/code/1" rel="stylesheet" type="text/css" />
<style type="text/css">
.chatBox {
position: relative;
margin: 1rem auto;
width: 80%;
height: 540px;
border: solid 1px rgb(164, 194, 236);
border-radius: 8px;
box-shadow: 0 0 3px 0 rgb(12, 12, 12);
}
.chatTop {
display: flex;
justify-content: space-between;
position: absolute;
z-index: 1;
width: 100%;
height: 2rem;
border-radius: 4px 4px 0 0;
box-shadow: 0 1px 1px 0 rgb(12, 12, 12, 0.5);
background: linear-gradient(rgb(164, 194, 236), rgb(92, 149, 230));
}
.chatTop span {
display: block;
margin: auto 8px;
font-family: 'Russo One', "Noto Sans SC", sans-serif;
font-weight: bold;
}
.chatBody {
--padding: 2rem;
padding-top: var(--padding);
padding-bottom: var(--padding);
height: calc(100% - 2 * var(--padding));
border-radius: 8px;
background: rgb(236, 236, 236) url('https://scp-wiki.wdfiles.com/local--files/theme%3Agoc/goc-fade.png') center no-repeat;
}
.chatItem {
display: flex;
margin: 1rem 0;
transform-origin: left bottom;
transform: scale(1);
font-family: PTRootUI,"方体","PingFang SC","黑体","Heiti SC","Microsoft JhengHei UI","Microsoft JhengHei",Roboto,Noto,"Noto Sans CJK SC",sans-serif;
animation: messageInput 300ms;
}
.chatItem.right {
justify-content: end;
transform-origin: right bottom;
}
.head {
display: block;
margin: 8px;
width: 12%;
max-width: 64px;
height: 12%;
max-height: 64px;
border-radius: 6px;
background-color: rgb(252, 252, 252);
}
.chatItemBody {
display: block;
margin-top: 18px;
max-width: 55%;
}
.name {
display: block;
font-size: .8em;
}
.right .name {
text-align: right;
}
.message {
display: inline-block;
margin-top: 6px;
padding: 6px;
border-radius: 6px;
border-top-left-radius: 0;
background-color: rgb(252, 252, 252);
}
.right .message {
border-top-left-radius: 6px;
border-top-right-radius: 0;
}
.markMessage {
color: rgb(96, 96, 96);
text-align: center;
font-size: .8em;
font-family: 'Russo One', "Noto Sans SC", sans-serif;
}
.radioButton {
--border-width: 3px;
display: flex;
justify-content: space-around;
position: absolute;
bottom: 0;
width: calc(100% - 2 * var(--border-width));
border: solid var(--border-width) rgb(252, 252, 252);
border-radius: 0 0 8px 8px;
background: rgb(252, 252, 252);
}
.radioButton button {
width: 40%;
font-family: 'Russo One', "Noto Sans SC", sans-serif !important;
font-weight: bold !important;
}
@keyframes messageInput {
0% {transform: scale(0);}
100% {transform: scale(1);}
}
</style>
</head>
<body>
<div class="chatBox">
<div class="chatTop">
<span><</span>
<span>聊天室标题</span>
<span>≡</span>
</div>
<div class="chatBody">
<div class="chatItem">
<img src="http://scp-wiki.wikidot.com/local--files/art:goc-logos/High-Command.png" class="head" />
<div class="chatItemBody">
<span class="name">昵称</span>
<span class="message">消息文字。</span>
</div>
</div>
<div class="chatItem right">
<div class="chatItemBody">
<span class="name">【发送】的昵称</span>
<span class="message">【发送】的消息文字。</span>
</div>
<img src="http://scp-wiki.wikidot.com/local--files/art:goc-logos/Council-of-108.png" class="head" />
</div>
<div class="markMessage">一条提示消息</div>
<div class="radioButton">
<button onclick="a()">选项A</button>
<button onclick="b()">选项B</button>
</div>
</div>
</div>
<script type="text/javascript">
var chatBody = document.getElementsByClassName("chatBody")[0]
var radioButton = document.getElementsByClassName("radioButton")[0]
function a() {
radioButton.remove()
let chatItem = document.createElement("div")
chatItem.classList.add("chatItem", "right")
let chatItemBody = document.createElement("div")
chatItemBody.classList.add("chatItemBody")
let headImage = document.createElement("img")
headImage.classList.add("head")
headImage.src = "http://scp-wiki.wikidot.com/local--files/art:goc-logos/Council-of-108.png"
let name = document.createElement("span")
name.classList.add("name")
name.innerHTML = "【发送】的昵称A"
chatItemBody.append(name)
let message = document.createElement("span")
message.classList.add("message")
message.innerHTML = "【发送】的消息内容A。"
chatItemBody.append(message)
chatItem.append(chatItemBody)
chatItem.append(headImage)
chatBody.append(chatItem)
setTimeout(c, 1000)
}
function c() {
let chatItem = document.createElement("div")
chatItem.classList.add("chatItem")
let chatItemBody = document.createElement("div")
chatItemBody.classList.add("chatItemBody")
let headImage = document.createElement("img")
headImage.classList.add("head")
headImage.src = "http://scp-wiki.wikidot.com/local--files/art:goc-logos/High-Command.png"
let name = document.createElement("span")
name.classList.add("name")
name.innerHTML = "昵称A"
chatItemBody.append(name)
let message = document.createElement("span")
message.classList.add("message")
message.innerHTML = "消息内容A。"
chatItemBody.append(message)
chatItem.append(headImage)
chatItem.append(chatItemBody)
chatBody.append(chatItem)
}
function b() {
radioButton.remove()
let chatItem = document.createElement("div")
chatItem.classList.add("chatItem", "right")
let chatItemBody = document.createElement("div")
chatItemBody.classList.add("chatItemBody")
let headImage = document.createElement("img")
headImage.classList.add("head")
headImage.src = "http://scp-wiki.wikidot.com/local--files/art:goc-logos/Council-of-108.png"
let name = document.createElement("span")
name.classList.add("name")
name.innerHTML = "【发送】的昵称B"
chatItemBody.append(name)
let message = document.createElement("span")
message.classList.add("message")
message.innerHTML = "【发送】的消息内容B。"
chatItemBody.append(message)
chatItem.append(chatItemBody)
chatItem.append(headImage)
chatBody.append(chatItem)
setTimeout(d, 1000)
}
function d() {
let chatItem = document.createElement("div")
chatItem.classList.add("chatItem")
let chatItemBody = document.createElement("div")
chatItemBody.classList.add("chatItemBody")
let headImage = document.createElement("img")
headImage.classList.add("head")
headImage.src = "http://scp-wiki.wikidot.com/local--files/art:goc-logos/High-Command.png"
let name = document.createElement("span")
name.classList.add("name")
name.innerHTML = "昵称B"
chatItemBody.append(name)
let message = document.createElement("span")
message.classList.add("message")
message.innerHTML = "消息内容B。"
chatItemBody.append(message)
chatItem.append(headImage)
chatItem.append(chatItemBody)
chatBody.append(chatItem)
}
</script>
</body>
[[/html]]
其中:
- transform-origin: 设置变换起始点,即 transform 属性的中心,默认为元素的中点。
- transform: 设置变换,包括:
- translate(): 位置变换。
- scale(): 缩放变换。
- rotate(): 旋转变换。
- skew(): 倾斜变换。
- matrix(): 矩阵变换。
以上就是对动态类聊天记录功能的实现。
当然了,我这里也制作了静态版,使用方法如下:
[[module CSS]]
.chatBox {
position: relative;
margin: 1rem auto;
width: 80%;
height: 540px;
border: solid 1px rgb(164, 194, 236);
border-radius: 8px;
box-shadow: 0 0 3px 0 rgb(12, 12, 12);
}
.chatTop p {
display: flex;
justify-content: space-between;
position: absolute;
top: -16px;
z-index: 1;
width: 100%;
height: 2rem;
border-radius: 4px 4px 0 0;
box-shadow: 0 1px 1px 0 rgb(12, 12, 12, 0.5);
background: linear-gradient(rgb(164, 194, 236), rgb(92, 149, 230));
}
.chatTop span {
display: block;
margin: auto 8px;
font-family: 'Russo One', "Noto Sans SC", sans-serif;
font-weight: bold;
}
.chatBody {
--padding: 2rem;
padding-top: var(--padding);
padding-bottom: var(--padding);
width: 100%;
height: calc(100% - 2 * var(--padding));
border-radius: 8px;
background: rgb(236, 236, 236) url('https://scp-wiki.wdfiles.com/local--files/theme%3Agoc/goc-fade.png') center no-repeat;
overflow-y: auto;
}
.chatItem {
display: flex;
margin: 1rem 0;
transform-origin: left bottom;
transform: scale(1);
font-family: PTRootUI,"方体","PingFang SC","黑体","Heiti SC","Microsoft JhengHei UI","Microsoft JhengHei",Roboto,Noto,"Noto Sans CJK SC",sans-serif;
animation: messageInput 300ms;
}
.chatItem.right {
justify-content: end;
transform-origin: right bottom;
}
.head {
display: block;
margin: 8px;
width: 12%;
max-width: 64px;
height: 12%;
max-height: 64px;
border-radius: 6px;
background-color: rgb(252, 252, 252);
}
.chatItemBody {
max-width: 55% !important;
}
.chatItemBody p {
display: block;
margin-top: 18px;
}
.name {
display: block;
font-size: .8em;
}
.right .name {
text-align: right;
}
.message {
display: inline-block;
margin-top: 6px;
padding: 6px;
border-radius: 6px;
border-top-left-radius: 0;
background-color: rgb(252, 252, 252);
}
.right .message {
border-top-left-radius: 6px;
border-top-right-radius: 0;
}
.markMessage {
color: rgb(96, 96, 96);
text-align: center;
font-size: .8em;
font-family: 'Russo One', "Noto Sans SC", sans-serif;
}
@keyframes messageInput {
0% {transform: scale(0);}
100% {transform: scale(1);}
}
[[/module]]
[[div class="chatBox"]]
[[div class="chatTop"]]
[[span]]<[[/span]][[span]]聊天室标题[[/span]][[span]]≡[[/span]]
[[/div]]
[[div class="chatBody"]]
[[include :goc-sandbox-cn:chat-item
| type=0
| headImage=http://scp-wiki.wikidot.com/local--files/art:goc-logos/High-Command.png
| name=最高指挥部
| text=第二人称演示文本。
]]
[[include :goc-sandbox-cn:chat-item
| type=1
| headImage=http://goc-sandbox-cn.wikidot.com/local--files/code-help/xiu.png
| name=荷修
| text=第一人称演示文本。
]]
[[include :goc-sandbox-cn:chat-item
| type=0
| headImage=http://scp-wiki.wikidot.com/local--files/art:goc-logos/High-Command.png
| name=最高指挥部
| text=又一条第二人称演示文本。
]]
[[div class="markMessage"]]
一段提示消息
[[/div]]
[[/div]]
[[/div]]
其中:
- [[include :goc-sandbox-cn:chat-item]]: 显示一条消息,可有可无,亦可根据需求添加多条。
- type: 设置消息源自左侧还是右侧。
- 0: 左侧。
- 1:右侧。
- headImage: 设置头像的 URL 。
- name: 设置昵称。
- text: 设置消息内容。
- type: 设置消息源自左侧还是右侧。
- [[div class="markMessage"]]: 设置一条提示消息,可有可无,亦可根据需求添加多条。
静态版的消息数量没有限制,但出于方便考虑,此处仅设置三条消息以作演示。
希望大家可以发挥出更多更好的创意来。



