Atom
本站架站紀錄_第六篇

本站架站紀錄_第六篇

新增網站字卡、語音、選擇題

因為想說要準備多益,原本只寫的單字在文章內,結果功能越寫越多英文都沒在看= =
真的是……唉……
為了方便記憶新增了翻轉字卡與語音
以及加入選擇題功能

字卡

新增卡片

在source中新增flipcard.css

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
.flip-card {
background-color: transparent;
width: 200px;
height: 120px;
perspective: 1000px;
display: inline-block;
margin: 10px;
cursor: pointer;
}

.flip-card-inner {
position: relative;
width: 100%;
height: 100%;
text-align: center;
transition: transform 0.6s;
transform-style: preserve-3d;
}

.flip-card:hover .flip-card-inner,
.flip-card.flipped .flip-card-inner {
transform: rotateY(180deg);
}

.flip-card-front, .flip-card-back {
position: absolute;
width: 100%;
height: 100%;
backface-visibility: hidden;
border: 2px solid #ccc;
border-radius: 10px;
display: flex;
justify-content: center;
align-items: center;
font-size: 1.2em;
background: #fff;
}

.flip-card-back {
transform: rotateY(180deg);
background: #f3f3f3;
}

在header中套用

1
<link rel="stylesheet" href="<%= url_for('/flipcard.css') %>">

新增翻面效果

在source中新增flipcard.js

1
2
3
4
5
6
7
document.addEventListener('DOMContentLoaded', () => {
document.querySelectorAll('.flip-card').forEach(card => {
card.addEventListener('click', () => {
card.classList.toggle('flipped');
});
});
});

同樣,在header中套用

1
<script src="<%= url_for('/js/flipcard.js') %>"></script>

新增使用標籤

在scripts中新增flipcard.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
'use strict';

hexo.extend.tag.register('flipcard', function (args) {
const front = args[0];
const back = args[1];
return `
<div class="flip-card">
<div class="flip-card-inner">
<div class="flip-card-front">${front}</div>
<div class="flip-card-back">${back}</div>
</div>
</div>
`;
});

這樣就可以在文章中使用翻轉字卡的功能了。

實際使用

我是一個
輸家
1
{% flipcard 正面 反面 %}

語音

使用的是瀏覽器內建的語音模式,因此要是瀏覽器本身不支援語音(或是已關閉)的話就不會有聲音

在source底下新增speak.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// speak.js
document.addEventListener('DOMContentLoaded', function () {
document.addEventListener('click', function (e) {
if (e.target.classList.contains('play-sound')) {
const word = e.target.dataset.word;
if (!word) return;
const utterance = new SpeechSynthesisUtterance(word);
utterance.lang = 'en-US'; // 英文語系,可改 en-GB / en-AU
utterance.rate = 0.9; // 語速
utterance.pitch = 1.0; // 音調
speechSynthesis.cancel(); // 避免多重播放
speechSynthesis.speak(utterance);
}
});
});

加入喇叭樣式
在source中新增custom.styl

實際顯示的符號會因為瀏覽器的預設樣式而有所不同

1
2
3
4
5
6
7
8
9
10
11
12
13
.play-sound {
background: none;
border: none;
color: #81b1b1; /* 海沫青(主題色) */
cursor: pointer;
font-size: 1em;
margin-left: 4px;
transition: transform 0.2s ease;
}
.play-sound:hover {
transform: scale(1.2);
color: #bcad76; /* 暖金 hover 效果 */
}

套用到header上

1
<script src="<%= url_for('/speak.js') %>"></script>

實際使用

eligible

1
<button class="play-sound" data-word="eligible">🔊</button>  

選擇題

新增quiz-card.js跟quiz-card.css

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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
(function () {
function initQuizCards() {
var rawCards = document.querySelectorAll('.toeic-quiz');
if (!rawCards.length) return;

rawCards.forEach(function (src, index) {
var question = src.getAttribute('data-question');
var answer = (src.getAttribute('data-answer') || '').trim().toUpperCase();
var explanation = src.getAttribute('data-explain') || '';

var options = [];
['A', 'B', 'C', 'D', 'E'].forEach(function (letter) {
var text = src.getAttribute('data-' + letter.toLowerCase());
if (text) options.push({ letter: letter, text: text });
});

if (!question || !answer || !options.length) return;

// 建立卡片骨架
var card = document.createElement('div');
card.className = 'quiz-card';
card.setAttribute('data-answer', answer);

var inner = document.createElement('div');
inner.className = 'quiz-card-inner';

var front = document.createElement('div');
front.className = 'quiz-card-face quiz-card-front';

var qEl = document.createElement('div');
qEl.className = 'quiz-question';
qEl.textContent = question;
front.appendChild(qEl);

var list = document.createElement('ul');
list.className = 'quiz-options';
var radioName = 'quiz-' + (index + 1) + '-' + Date.now();

options.forEach(function (opt) {
var li = document.createElement('li');
var label = document.createElement('label');
var input = document.createElement('input');
input.type = 'radio';
input.name = radioName;
input.value = opt.letter;

var textSpan = document.createElement('span');
textSpan.className = 'quiz-option-text';
textSpan.textContent = opt.letter + '. ' + opt.text;

label.appendChild(input);
label.appendChild(textSpan);
li.appendChild(label);
list.appendChild(li);
});
front.appendChild(list);

var result = document.createElement('div');
result.className = 'quiz-result';
front.appendChild(result);

var btnBox = document.createElement('div');
btnBox.className = 'quiz-btn-box';

var btnCheck = document.createElement('button');
btnCheck.type = 'button';
btnCheck.className = 'quiz-btn quiz-btn-check';
btnCheck.textContent = '查看答案';
btnBox.appendChild(btnCheck);

var btnExplain = document.createElement('button');
btnExplain.type = 'button';
btnExplain.className = 'quiz-btn quiz-btn-explain hidden';
btnExplain.textContent = '查看解釋';
btnBox.appendChild(btnExplain);

front.appendChild(btnBox);

var back = document.createElement('div');
back.className = 'quiz-card-face quiz-card-back';

var explainText = document.createElement('div');
explainText.className = 'quiz-explain';
explainText.textContent = explanation || '此題尚無解釋~';
back.appendChild(explainText);

var btnBack = document.createElement('button');
btnBack.type = 'button';
btnBack.className = 'quiz-btn quiz-btn-back';
btnBack.textContent = '返回題目';
back.appendChild(btnBack);

inner.appendChild(front);
inner.appendChild(back);
card.appendChild(inner);
src.parentNode.replaceChild(card, src);

// 查看答案
btnCheck.addEventListener('click', function () {
var checked = card.querySelector('input[type="radio"]:checked');
if (!checked) {
result.textContent = '請先選一個選項~';
result.classList.remove('is-correct', 'is-wrong');
btnExplain.classList.add('hidden');
return;
}

var isCorrect = checked.value === answer;
result.textContent = isCorrect
? '✔ 回答正確!'
: '✘ 錯誤,請再選一次';
result.classList.toggle('is-correct', isCorrect);
result.classList.toggle('is-wrong', !isCorrect);
btnExplain.classList.remove('hidden');
});

// 查看解釋(翻面)
btnExplain.addEventListener('click', function () {
inner.classList.add('is-flipped');
});

// 返回題目
btnBack.addEventListener('click', function () {
inner.classList.remove('is-flipped');
});
});
}

if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initQuizCards);
} else {
initQuizCards();
}
})();
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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
.quiz-card {
margin: 1.5rem 0;
perspective: 1000px;
}

.quiz-card-inner {
position: relative;
width: 100%;
background: var(--kira-card-bg, #fff);
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
transform-style: preserve-3d;
transition: transform 0.5s;
}

.quiz-card-inner.is-flipped {
transform: rotateY(180deg);
}

.quiz-card-face {
position: relative;
padding: 1rem 1.25rem;
backface-visibility: hidden;
}

.quiz-card-front {
}

.quiz-card-back {
position: absolute;
inset: 0;
transform: rotateY(180deg);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
}

.quiz-question {
font-weight: 600;
margin-bottom: 0.75rem;
}

.quiz-options {
list-style: none;
padding: 0;
margin: 0 0 0.75rem;
}

.quiz-options li + li {
margin-top: 0.4rem;
}

.quiz-options label {
display: flex;
align-items: center;
gap: 0.4rem;
cursor: pointer;
}

.quiz-options input[type='radio'] {
accent-color: var(--kira-primary, #6599b3);
}

.quiz-option-text {
flex: 1;
}

.quiz-btn {
display: inline-block;
padding: 0.35rem 0.9rem;
font-size: 0.85rem;
border-radius: 999px;
border: 1px solid var(--kira-primary, #6599b3);
background: var(--kira-primary, #6599b3);
color: #fff;
cursor: pointer;
}

.quiz-btn + .quiz-btn {
margin-left: 0.5rem;
}

.quiz-btn-back {
margin-top: 0.75rem;
background: #fff;
color: var(--kira-primary, #6599b3);
}

.quiz-result {
font-weight: 600;
}

.quiz-result.is-correct {
color: #2e8b57;
}

.quiz-result.is-wrong {
color: #b22222;
}
.quiz-btn-box {
display: flex;
gap: 0.5rem;
margin-top: 0.75rem;
}

.quiz-btn-explain.hidden {
display: none;
}

.quiz-explain {
max-width: 90%;
font-size: 0.95rem;
line-height: 1.6;
color: #333;
background: rgba(255,255,255,0.8);
border-radius: 8px;
padding: 1rem;
box-shadow: 0 2px 6px rgba(0,0,0,0.08);
}

同樣在header套用

1
2
<link rel="stylesheet" href="<%= url_for('/quiz-card.css') %>">
<script src="<%= url_for('/js/quiz-card.js') %>"></script>

實際使用

1
2
3
4
5
6
7
8
<div class="toeic-quiz"
data-question="Management will hold the quarterly meeting on ____."
data-a="last Friday"
data-b="this Friday"
data-c="next Friday"
data-d="every Friday"
data-answer="C"
data-explain="因為 quarterly meeting 是定期舉行的會議,通常用未來時間,因此答案為 next Friday。"></div>
本文作者:Atom
本文鏈接:https://d0ngd.github.io/2025/11/11/本站架站紀錄_第六篇/
版權聲明:本文採用 CC BY-NC-SA 3.0 CN 協議進行許可