-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathindex.html
More file actions
1310 lines (1228 loc) · 63.1 KB
/
Copy pathindex.html
File metadata and controls
1310 lines (1228 loc) · 63.1 KB
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-L4CMDLM3DL"></script>
<script>window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments);}gtag('js',new Date());gtag('config','G-L4CMDLM3DL');</script>
<script>(function(){try{var t=localStorage.getItem('hsk4_theme');if(t==='dark'||(!t&&window.matchMedia&&matchMedia('(prefers-color-scheme: dark)').matches))document.documentElement.setAttribute('data-theme','dark');}catch(e){}})();</script>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<title>HSK 4 Mock Exam 2026 — 19 Free Practice Tests | Mandarin Zone</title>
<meta name="description" content="Pass HSK 4 — 19 free mock exams (1,875 questions, auto-scored) including official Hanban papers H41220, H41221, H41327, H41328, H41329, H41001, and H41330, plus a 1,000-word vocab list and 14 grammar topics. 2026 syllabus, by Mandarin Zone Beijing.">
<meta name="keywords" content="HSK 4 mock exam, HSK 4 practice test, HSK4 模拟考试, HSK 4 listening, HSK 4 reading, HSK 4 writing, HSK 4 vocabulary, HSK 4 grammar, HSK 4 2026, free HSK practice">
<link rel="canonical" href="https://hsk4.mandarinzone.com/">
<!-- Open Graph -->
<meta property="og:title" content="HSK 4 Mock Exam 2026 — 19 Free Practice Tests + Complete Study Toolkit">
<meta property="og:description" content="Pass HSK 4 with confidence — 19 free mock exams (1,875 questions, auto-scored), a 1,000-word vocab list, 14 grammar topics, and 30 task scenarios. 2026 syllabus, by Mandarin Zone Beijing.">
<meta property="og:type" content="website">
<meta property="og:url" content="https://hsk4.mandarinzone.com/">
<meta property="og:site_name" content="Mandarin Zone">
<meta property="og:locale" content="en_US">
<meta property="og:locale:alternate" content="zh_CN">
<!-- og:image: using the Mandarin Zone parent-brand logo as fallback. A
dedicated 1200×630 social card would convert better; replace this URL
once that asset exists. -->
<meta property="og:image" content="https://www.mandarinzone.com/wp-content/uploads/2015/01/logo.png">
<meta property="og:image:alt" content="Mandarin Zone — HSK 4 Mock Exam 2026, 19 free practice tests">
<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="HSK 4 Mock Exam 2026 — 19 Free Practice Tests + Toolkit">
<meta name="twitter:description" content="Pass HSK 4 with confidence — 19 free mock exams (1,875 questions), 1,000-word vocab, and 14 grammar topics. 2026 syllabus, by Mandarin Zone.">
<meta name="twitter:image" content="https://www.mandarinzone.com/wp-content/uploads/2015/01/logo.png">
<meta name="twitter:image:alt" content="Mandarin Zone — HSK 4 Mock Exam 2026">
<!-- Structured Data -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "WebApplication",
"name": "HSK 4 Mock Exam",
"description": "Free online HSK 4 practice tests with 19 complete mock exams covering listening, reading, and writing sections.",
"url": "https://hsk4.mandarinzone.com/",
"applicationCategory": "EducationalApplication",
"operatingSystem": "Any",
"offers": {
"@type": "Offer",
"price": "0",
"priceCurrency": "USD"
},
"author": {
"@type": "Organization",
"name": "Mandarin Zone",
"url": "https://mandarinzone.com",
"foundingDate": "2008",
"address": {
"@type": "PostalAddress",
"addressLocality": "Beijing",
"addressCountry": "CN"
}
},
"inLanguage": ["en", "zh-CN"],
"isAccessibleForFree": true,
"educationalLevel": "Intermediate",
"about": {
"@type": "Thing",
"name": "HSK 4",
"description": "Hanyu Shuiping Kaoshi Level 4 Chinese Proficiency Test"
}
}
</script>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "LearningResource",
"name": "HSK 4 Mock Exam — Free Practice Tests + Complete Study Toolkit",
"description": "19 free HSK 4 mock exams with 1,875 questions covering listening, reading, and writing. Plus 1,000 vocabulary, 14 grammar topics, 30 task scenarios. Aligned with the 2026 official syllabus.",
"url": "https://hsk4.mandarinzone.com/",
"learningResourceType": ["Practice Test", "Quiz", "Study Guide"],
"educationalLevel": "Intermediate",
"teaches": "Chinese language proficiency at HSK Level 4 (intermediate Mandarin)",
"inLanguage": ["en", "zh-CN"],
"isAccessibleForFree": true,
"audience": {
"@type": "EducationalAudience",
"educationalRole": "student"
},
"provider": {
"@type": "Organization",
"name": "Mandarin Zone",
"url": "https://mandarinzone.com"
},
"about": {
"@type": "Thing",
"name": "HSK 4",
"description": "Hanyu Shuiping Kaoshi Level 4 Chinese Proficiency Test"
}
}
</script>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [
{
"@type": "Question",
"name": "What is HSK 4?",
"acceptedAnswer": {
"@type": "Answer",
"text": "HSK 4 (Hanyu Shuiping Kaoshi Level 4) is an intermediate-level Chinese proficiency test. Passing HSK 4 means you can discuss a wide range of topics in Chinese. The current exam (through June 2026) tests about 1,200 words; the revised 2026 syllabus expects 2,000 cumulative words, of which 1,000 are new at Level 4."
}
},
{
"@type": "Question",
"name": "How many questions are in the HSK 4 exam?",
"acceptedAnswer": {
"@type": "Answer",
"text": "The current HSK 4 exam (administered through June 2026) has 100 questions divided into three sections: Listening (45 questions), Reading (40 questions), and Writing (15 questions). The total test time is approximately 105 minutes. From July 2026 the revised syllabus (HSK 3.0) takes effect — our mock exams follow the current format."
}
},
{
"@type": "Question",
"name": "Are these HSK 4 practice tests free?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Yes, all 19 HSK 4 mock exams are completely free. The tests are open-source and licensed under CC BY-NC-SA 4.0 by Mandarin Zone."
}
},
{
"@type": "Question",
"name": "What is the HSK 4 pass mark?",
"acceptedAnswer": {
"@type": "Answer",
"text": "The HSK 4 pass mark is 180 out of 300 (60%). However, many universities and visa programs require a higher score, often 240+ (80%). Aim for 70%+ on these mock exams to give yourself a comfortable margin."
}
},
{
"@type": "Question",
"name": "How long does it take to prepare for HSK 4?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Most learners pass HSK 4 in 8 weeks of focused work, given a solid HSK 3 foundation. Take one mock exam per week in weeks 1–4, focus on your weakest section in weeks 5–8, and run 2–3 timed full tests in the final 2 weeks to build exam stamina."
}
},
{
"@type": "Question",
"name": "What changed in the 2026 HSK 4 syllabus?",
"acceptedAnswer": {
"@type": "Answer",
"text": "The 2026 syllabus (published November 2025, effective July 2026) raises the bar with 30 communicative tasks requiring 'a certain level of complexity', a 2,000-word cumulative vocabulary (1,000 new at Level 4), 441 reading characters and 150 handwriting characters, and new grammar patterns: 把字句2 (four new structures), 被动句2 with 叫/让, 兼语句2 (causative/evaluative), 比较句3 (A不如B, 跟…相比), 双重否定句, plus complex sentence types (concessive, conditional, hypothetical)."
}
}
]
}
</script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;700&family=Noto+Serif+SC:wght@400;700&family=DM+Sans:wght@400;500;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/common.css">
<link rel="stylesheet" href="/homepage.css">
</head>
<body>
<header>
<div class="header-inner">
<a href="/" class="logo">
<img src="https://www.mandarinzone.com/wp-content/uploads/2015/01/logo.png" alt="Mandarin Zone" class="logo-mark" loading="eager">
<div class="logo-text">HSK 4 <span>Mock Exam</span></div>
</a>
<input type="checkbox" id="nav-toggle" class="nav-toggle" aria-label="Menu">
<label for="nav-toggle" class="nav-burger" aria-hidden="true"><span class="nav-burger-bar"></span></label>
<nav class="site-nav" aria-label="Primary">
<a href="/vocabulary/" class="nav-link">Vocabulary</a>
<a href="/characters/" class="nav-link">Characters</a>
<a href="/grammar/" class="nav-link">Grammar</a>
<a href="/sentences/" class="nav-link">Sentences</a>
<a href="/strategies/" class="nav-link">Strategies</a>
<a href="/topics/" class="nav-link">Topics</a>
<a href="/words/" class="nav-link">Words</a>
<a href="/compare/" class="nav-link">Compare</a>
<a href="/traps/" class="nav-link">Traps</a>
<a href="/guide/" class="nav-link">Guide</a>
<a href="https://github.com/Make-dream-clear/hsk4-mock-exam" class="gh-link" target="_blank" rel="noopener">
<svg viewBox="0 0 16 16"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/></svg>
GitHub
</a>
</nav>
</div>
</header>
<main>
<!-- HOME SCREEN -->
<div id="home-screen" class="screen active">
<!-- ===== HERO ===== -->
<div class="hero">
<div class="hero-badge">Free · Open Source · 2026 Syllabus</div>
<!-- H1 is bilingual: English keyword "Mock Exam" matches the title tag
and is what English-language searchers will scan for; Chinese
label is set in red and tagged lang="zh-CN" so screen readers
switch voices and Google can index it correctly. Both languages
inherit `.hero h1` Noto Serif SC for visual consistency — no
`.chinese` class on the span, since that would force a sans-serif
that breaks the heading's typographic harmony. -->
<h1>HSK 4 Mock Exam <span class="accent" lang="zh-CN">模拟考试</span></h1>
<p>The complete HSK 4 prep platform — 19 free mock exams plus a full study toolkit, aligned with the 2026 syllabus. By Mandarin Zone, Beijing.</p>
<div class="hero-cta">
<a href="/test/01/" class="btn btn-primary">Start Test 01 →</a>
<a href="#toolkit" class="btn btn-ghost">Browse the Toolkit</a>
</div>
<div class="stats-row">
<div class="stat"><div class="stat-num">19</div><div class="stat-label">Mock Exams</div></div>
<div class="stat"><div class="stat-num">1,875</div><div class="stat-label">Questions</div></div>
<div class="stat"><div class="stat-num">1,000</div><div class="stat-label">Vocabulary</div></div>
<div class="stat"><div class="stat-num">14</div><div class="stat-label">Grammar</div></div>
</div>
<p class="trust-line">Trusted by 5,000+ students from 40+ countries · 98% HSK pass rate · Mandarin Zone since 2008</p>
</div>
<!-- ===== CHOOSE YOUR PATH ===== -->
<section class="path-section" aria-labelledby="path-heading">
<h2 class="section-title" id="path-heading">Where to start</h2>
<div class="path-grid">
<a href="/test/01/" class="path-card">
<div class="path-icon" aria-hidden="true">🎯</div>
<div class="path-stage">Step 1 · Diagnose</div>
<h3>Take a diagnostic test</h3>
<p>Start with Test 01 (Sample Quiz, 100 questions) to find your baseline score and spot your weakest section.</p>
<span class="path-cta">Take Test 01 →</span>
</a>
<a href="#toolkit" class="path-card">
<div class="path-icon" aria-hidden="true">📚</div>
<div class="path-stage">Step 2 · Build</div>
<h3>Strengthen your foundation</h3>
<p>Vocabulary, grammar patterns, sentence templates, and topic-specific drills to fix the gaps your diagnostic revealed.</p>
<span class="path-cta">Browse the toolkit →</span>
</a>
<a href="/train/" class="path-card">
<div class="path-icon" aria-hidden="true">⏱</div>
<div class="path-stage">Step 3 · Practice</div>
<h3>Drill every skill</h3>
<p>The Practice Center gathers all 19 mock exams plus reading, writing, vocab and character drills — with your progress saved.</p>
<span class="path-cta">Open the Practice Center →</span>
</a>
</div>
</section>
<!-- ===== MOCK EXAMS ===== -->
<section id="mock-exams" aria-labelledby="mock-exams-heading">
<h2 class="section-title" id="mock-exams-heading">19 Free Mock Exams</h2>
<p class="section-intro">All 19 tests include the three exam sections (听力 + 阅读 + 书写) and auto-grade with instant results. Most are full 100-question papers — including the official Hanban papers (with the real listening audio and full transcripts) — while Test 04 has 99 questions and Test 07 is a 76-question partial set.</p>
<div id="test-grid" class="test-grid">
<div class="loading"><div class="spinner"></div>
<noscript>
<div style="margin:20px 0;">
<h2 style="font-size:18px;margin-bottom:12px;">Available Tests:</h2>
<ul style="line-height:2;padding-left:20px;">
<li><a href="/test/01/">HSK 4 Mock Test 01 — Sample Quiz (100 questions)</a></li>
<li><a href="/test/02/">HSK 4 Mock Test 02 (100 questions)</a></li>
<li><a href="/test/03/">HSK 4 Mock Test 03 (H41002) (100 questions)</a></li>
<li><a href="/test/04/">HSK 4 Mock Test 04 (99 questions)</a></li>
<li><a href="/test/05/">HSK 4 Mock Test 05 (100 questions)</a></li>
<li><a href="/test/06/">HSK 4 Mock Test 06 (H41006) (100 questions)</a></li>
<li><a href="/test/07/">HSK 4 Mock Test 07 (H41007 partial) (76 questions)</a></li>
<li><a href="/test/08/">HSK 4 Mock Test 08 (H41008) (100 questions)</a></li>
<li><a href="/test/09/">HSK 4 Mock Test 09 (H41009) (100 questions)</a></li>
<li><a href="/test/10/">HSK 4 Mock Test 10 (H41010) (100 questions)</a></li>
<li><a href="/test/11/">HSK 4 Mock Test 11 (H41111) (100 questions)</a></li>
<li><a href="/test/12/">HSK 4 Mock Test 12 (100 questions)</a></li>
<li><a href="/test/13/">HSK 4 Official Exam 13 (H41220) (100 questions)</a></li>
<li><a href="/test/14/">HSK 4 Official Exam 14 (H41221) (100 questions)</a></li>
<li><a href="/test/15/">HSK 4 Official Exam 15 (H41327) (100 questions)</a></li>
<li><a href="/test/16/">HSK 4 Official Exam 16 (H41328) (100 questions)</a></li>
<li><a href="/test/17/">HSK 4 Official Exam 17 (H41329) (100 questions)</a></li>
<li><a href="/test/18/">HSK 4 Official Exam 18 (H41001) (100 questions)</a></li>
<li><a href="/test/19/">HSK 4 Official Exam 19 (H41330) (100 questions)</a></li>
</ul>
</div>
</noscript>
Loading tests...</div>
</div>
</section>
<!-- STATIC SEO CONTENT -->
<section id="toolkit" aria-labelledby="toolkit-heading">
<h2 class="section-title" id="toolkit-heading">Complete HSK 4 Toolkit</h2>
<p class="section-intro">Mock exams alone won't get you to 180/300 — you need to build the underlying language. Here's everything we offer, grouped by what it does for your score.</p>
<div class="toolkit-group toolkit-group--foundation">
<h3 class="toolkit-group-title">📚 Foundation — Words, patterns, and topics</h3>
<p class="toolkit-group-sub">The raw material. Without these, no strategy will save you.</p>
<div class="toolkit-cards">
<a href="/vocabulary/" class="toolkit-card">
<div class="toolkit-card-tag">Vocab</div>
<h4>1,000 HSK 4 Words</h4>
<p>Complete word list with pinyin, examples, and topic tags. Aligned with the 2026 syllabus.</p>
</a>
<a href="/grammar/" class="toolkit-card">
<div class="toolkit-card-tag">Grammar</div>
<h4>14 Grammar Topics</h4>
<p>把字句, 被字句, 比较句, complements, complex sentences, measure words and more.</p>
</a>
<a href="/sentences/" class="toolkit-card">
<div class="toolkit-card-tag">Sentences</div>
<h4>100 Essential Sentences</h4>
<p>High-frequency templates for opinion, suggestion, comparison, and time — ready for the writing section.</p>
</a>
<a href="/topics/" class="toolkit-card">
<div class="toolkit-card-tag">Scenarios</div>
<h4>30 Topic Scenarios</h4>
<p>Vocabulary by communicative situation: family, work, health, food, technology…</p>
</a>
</div>
</div>
<div class="toolkit-group toolkit-group--precision">
<h3 class="toolkit-group-title">🔍 Precision — The details that win marks</h3>
<p class="toolkit-group-sub">Hand-picked traps and distinctions HSK loves to test.</p>
<div class="toolkit-cards">
<a href="/words/" class="toolkit-card">
<div class="toolkit-card-tag">Confusables</div>
<h4>43 Confusable Word Pairs</h4>
<p>才/就, 被/让/叫, 关于/对于, 从来/一直 and other tested distinctions.</p>
</a>
<a href="/grammar/measure-words/" class="toolkit-card">
<div class="toolkit-card-tag">Measure Words</div>
<h4>HSK 4 Measure Words</h4>
<p>8 new MW (打/袋/棵/台/幅/场/顿/趟) plus borrowed measure words and a quiz.</p>
</a>
<a href="/writing/sentence-order/" class="toolkit-card">
<div class="toolkit-card-tag">Writing Drill</div>
<h4>Sentence Ordering</h4>
<p>Targeted drills for the trickiest reading question type. Templates + answer keys.</p>
</a>
<a href="/practice/" class="toolkit-card">
<div class="toolkit-card-tag">Mixed Drill</div>
<h4>选词填空 Mixed Practice</h4>
<p>156 grammar + confusable-word questions shuffled like the real reading section, with instant scoring.</p>
</a>
</div>
</div>
<div class="toolkit-group toolkit-group--strategy">
<h3 class="toolkit-group-title">⚡ Strategy — How to actually pass</h3>
<p class="toolkit-group-sub">Test-day tactics. Worth +15–30 points at the same vocabulary level.</p>
<div class="toolkit-cards">
<a href="/strategies/" class="toolkit-card">
<div class="toolkit-card-tag">Strategy</div>
<h4>9 Strategy Guides</h4>
<p>Test-taking tips for all 7 question types + listening signal words + picture templates.</p>
</a>
<a href="/guide/" class="toolkit-card">
<div class="toolkit-card-tag">Guide</div>
<h4>HSK 4 Study Guide 2026</h4>
<p>Exam structure, scoring, study timeline, self-assessment checklist.</p>
</a>
<a href="/compare/hsk4-vs-hsk3/" class="toolkit-card">
<div class="toolkit-card-tag">Compare</div>
<h4>HSK 4 vs HSK 3</h4>
<p>What changes from HSK 3 to HSK 4: vocabulary, grammar, exam time, study weeks.</p>
</a>
<a href="/compare/hsk4-vs-hsk5/" class="toolkit-card">
<div class="toolkit-card-tag">Compare</div>
<h4>HSK 4 vs HSK 5</h4>
<p>After HSK 4: 1,300 new words, advanced grammar, full essay writing.</p>
</a>
<a href="/compare/new-vs-old-hsk4/" class="toolkit-card">
<div class="toolkit-card-tag">2026 Change</div>
<h4>New vs Old HSK 4</h4>
<p>What the July 2026 syllabus changes: 2,000 words, 150 handwriting characters, 30 tasks.</p>
</a>
</div>
</div>
<h2 class="section-title">HSK 4 Exam Format</h2>
<p class="section-intro">100 questions, 105 minutes total. The pass mark is 180/300 (60%) — but real-world programs and visa applications often look for 240+ (80%). This is the current format, administered through June 2026; from July 2026 the revised HSK 3.0 syllabus takes effect (our mock exams follow the current format).</p>
<div class="format-table-wrap">
<table class="format-table">
<thead>
<tr>
<th>Section</th>
<th>Questions</th>
<th>Time</th>
<th>What it tests</th>
</tr>
</thead>
<tbody>
<tr><td><span class="badge-pill badge-listening">听力 Listening</span></td><td>45</td><td>~30 min</td><td>True/false judgments, multiple choice from audio clips played once</td></tr>
<tr><td><span class="badge-pill badge-reading">阅读 Reading</span></td><td>40</td><td>40 min</td><td>Vocabulary fill-in, sentence ordering, passage comprehension</td></tr>
<tr><td><span class="badge-pill badge-writing">书写 Writing</span></td><td>15</td><td>25 min</td><td>Construct sentences from given words</td></tr>
<tr class="format-table-total"><td>Total</td><td>100</td><td>~105 min</td><td>Pass mark: 180/300 (60%)</td></tr>
</tbody>
</table>
</div>
<h2 class="section-title">What the 2026 Syllabus Demands</h2>
<p class="section-intro">The new HSK syllabus (《新版HSK考试大纲》, published November 2025, effective July 2026) raises the bar at Level 4. Unlike HSK 3 which focuses on basic daily needs, HSK 4 requires handling "有一定复杂度" (a certain level of complexity) across 30 communicative tasks, grouped here into five themes:</p>
<h3 class="subsection-title">30 Communicative Tasks</h3>
<div class="topics-grid">
<div class="topics-cluster">
<h4>👤 Personal & Social</h4>
<ul>
<li><a href="/topics/describe-a-person/">谈论某个人物 — Discuss a person</a></li>
<li><a href="/topics/social-expressions/">日常言语交往 — Daily verbal interactions</a></li>
<li><a href="/topics/emotions/">谈论情感话题 — Discuss emotions</a></li>
<li><a href="/topics/hobbies-leisure/">交流业余爱好、休闲度假 — Hobbies & leisure</a></li>
<li><a href="/topics/family-life/">交流家庭生活情况 — Family life</a></li>
<li><a href="/topics/housing-community/">交流居住情况、社区情况 — Housing & community</a></li>
</ul>
</div>
<div class="topics-cluster">
<h4>🏃 Daily Life</h4>
<ul>
<li><a href="/topics/daily-affairs/">交流、处理日常事务 — Handle daily affairs</a></li>
<li><a href="/topics/food-dining/">介绍饮食情况 — Food & dining</a></li>
<li><a href="/topics/transportation/">谈论交通出行 — Transportation</a></li>
<li><a href="/topics/shopping/">交流购物体验、商业活动内容 — Shopping experiences</a></li>
<li><a href="/topics/health-medical/">谈论就医情况、健康生活 — Health & medical</a></li>
<li><a href="/topics/sports/">谈论体育项目及比赛 — Sports</a></li>
</ul>
</div>
<div class="topics-cluster">
<h4>🎓 Education & Work</h4>
<ul>
<li><a href="/topics/education-learning/">谈论教学、学习情况 — Education & learning</a></li>
<li><a href="/topics/campus-life/">交流校园生活 — Campus life</a></li>
<li><a href="/topics/education-issues/">谈论教育现象、观念 — Education phenomena</a></li>
<li><a href="/topics/work-performance/">谈论工作情况与表现 — Work situations</a></li>
<li><a href="/topics/career-experience/">介绍职业经历与单位情况 — Career experiences</a></li>
</ul>
</div>
<div class="topics-cluster">
<h4>🌏 Society & World</h4>
<ul>
<li><a href="/topics/nature/">谈论自然情况 — Nature & geography</a></li>
<li><a href="/topics/environment/">谈论生活中的环保情况 — Environmental protection</a></li>
<li><a href="/topics/technology/">介绍新技术应用及科技成果 — Technology</a></li>
<li><a href="/topics/china-provinces/">介绍中国的主要省市、民族 — Chinese provinces & ethnicities</a></li>
<li><a href="/topics/economy/">谈论经济现象 — Economic phenomena</a></li>
<li><a href="/topics/social-phenomena/">谈论社会现象 — Social phenomena</a></li>
<li><a href="/topics/arts-entertainment/">介绍文艺形式、活动、作品 — Arts & entertainment</a></li>
<li><a href="/topics/international-friendship/">讲述中外友好故事 — China-world friendship</a></li>
</ul>
</div>
<div class="topics-cluster">
<h4>🏮 Culture & Tradition</h4>
<ul>
<li><a href="/topics/proverbs-sayings/">介绍常见俗语、名言 — Proverbs & sayings</a></li>
<li><a href="/topics/food-culture/">介绍传统饮食文化 — Traditional food culture</a></li>
<li><a href="/topics/customs-traditions/">介绍风俗传统 — Customs & traditions</a></li>
<li><a href="/topics/scenic-spots/">介绍名胜古迹 — Scenic spots & historic sites</a></li>
<li><a href="/topics/historical-figures/">介绍历史人物、历史事件 — Historical figures & events</a></li>
</ul>
</div>
</div>
<h3 class="subsection-title">New Grammar Patterns at Level 4</h3>
<div class="grammar-callout">
<p>The official grammar syllabus adds significant complexity at Level 4. The patterns below are the highest-leverage ones to master before sitting the test:</p>
<ul class="grammar-points">
<li><strong>把字句2</strong> — four new structures (tentative, completed, quantified, modified)</li>
<li><strong>被动句2</strong> — using 叫/让 instead of just 被</li>
<li><strong>兼语句2</strong> — causative and evaluative sentences</li>
<li><strong>比较句3</strong> — "A不如B" and "跟…相比"</li>
<li><strong>双重否定句</strong> — for emphasis</li>
<li><strong>复句</strong> — concessive (尽管…但是), conditional (不管…都, 无论…都), hypothetical (要是…否则)</li>
</ul>
<a href="/grammar/" class="learn-more">Practice each pattern in our grammar guide →</a>
</div>
<h2 class="section-title">Section-by-Section Tips</h2>
<p class="section-intro">Strategy advice from the question types most often missed. Combine these with the strategy guides in the toolkit above.</p>
<div class="tips-grid">
<div class="tip-card tip-card--listening">
<div class="tip-card-section">听力 Listening</div>
<h3>Listen for meaning, not just words</h3>
<p>The HSK 4 listening section plays each clip <strong>only once</strong>. The 判断对错 section tests inference — what the speaker really means, not what they literally said. Train yourself to ask "what does this imply?" rather than "what did I hear?"</p>
<a href="/strategies/listening-judgment/" class="tip-link">Listening strategies →</a>
</div>
<div class="tip-card tip-card--reading">
<div class="tip-card-section">阅读 Reading</div>
<h3>Learn collocations, not just words</h3>
<p>Fill-in-the-blank rewards collocations. Knowing 影响 means "influence" isn't enough — you need 对…产生影响. Sentence ordering follows structural templates: time/place → subject → action → result/comment.</p>
<a href="/vocabulary/" class="tip-link">Vocab with collocations →</a>
</div>
<div class="tip-card tip-card--writing">
<div class="tip-card-section">书写 Writing</div>
<h3>Memorize sentence templates</h3>
<p>The writing section asks you to build sentences from given words. Recognising common patterns (S+V+O+Result, Time-Place-Subject-Action) makes this section much faster. Drill the 100 essential sentences.</p>
<a href="/sentences/" class="tip-link">Essential sentences →</a>
</div>
</div>
<h2 class="section-title">8-Week Study Plan</h2>
<p class="section-intro">Most learners pass HSK 4 in 8 weeks of focused work, given a solid HSK 3 foundation. Here's the proven sequence — and what to do in each phase.</p>
<div class="plan-timeline">
<div class="plan-phase">
<div class="plan-phase-head">
<span class="plan-week">Weeks 1–4</span>
<span class="plan-phase-name">Build & Diagnose</span>
</div>
<p>Take one full mock per week under timed conditions. Spend <strong>twice as long</strong> reviewing wrong answers as you spent on the test — that's where learning happens. Build vocabulary on the side using flashcards.</p>
</div>
<div class="plan-phase">
<div class="plan-phase-head">
<span class="plan-week">Weeks 5–8</span>
<span class="plan-phase-name">Target weak section</span>
</div>
<p>Focus on your weakest section. Listening weak? Replay audio and shadow dialogues. Reading weak? Drill grammar patterns. Writing weak? Memorize sentence templates and ordering patterns.</p>
</div>
<div class="plan-phase">
<div class="plan-phase-head">
<span class="plan-week">Final 2 weeks</span>
<span class="plan-phase-name">Build exam stamina</span>
</div>
<p>Take 2–3 full tests back-to-back to simulate exam conditions. Aim for 70%+ consistently — that gives you a 10-point cushion above the 60% pass line on test day.</p>
</div>
</div>
</section>
<!-- ===== ABOUT MANDARIN ZONE ===== -->
<section class="about-section" aria-labelledby="about-heading">
<div class="about-card">
<img src="https://www.mandarinzone.com/wp-content/uploads/2015/01/logo.png" alt="Mandarin Zone — Chinese language school in Beijing" class="about-logo" loading="lazy" width="180" height="180">
<div class="about-body">
<h2 id="about-heading">Mandarin Zone <span class="about-since">· Since 2008</span></h2>
<p>These tests are created by <a href="https://www.mandarinzone.com/" target="_blank" rel="noopener">Mandarin Zone</a>, a Chinese language school in Beijing serving 5,000+ students from 40+ countries with a 98% HSK pass rate. Looking for a teacher? We offer 1-on-1 online and in-person classes with experienced HSK-prep instructors.</p>
<div class="about-actions">
<a href="https://www.mandarinzone.com/" target="_blank" rel="noopener" class="btn btn-primary">Visit Website</a>
<a href="https://www.mandarinzone.com/contact-us/" target="_blank" rel="noopener" class="btn btn-ghost">Contact Us</a>
</div>
</div>
</div>
</section>
<!-- ===== FINAL CTA ===== -->
<div class="cta-banner">
<h2 class="chinese" lang="zh-CN">想要更系统地学中文?</h2>
<p>1-on-1 online and in-person Chinese classes by Mandarin Zone — Beijing's trusted school since 2008.</p>
<a href="https://mandarinzone.com" target="_blank" rel="noopener" class="btn btn-primary">Start Learning at Mandarin Zone</a>
<a href="https://github.com/Make-dream-clear/hsk4-mock-exam" class="cta-link" target="_blank" rel="noopener">or star us on GitHub ⭐</a>
</div>
</div>
<!-- QUIZ SCREEN -->
<div id="quiz-screen" class="screen">
<div class="quiz-header">
<div class="quiz-title" id="quiz-title"></div>
<div class="quiz-header-right">
<button class="quiz-timer" id="quiz-timer" type="button" onclick="toggleTimer()" title="Tap to pause / resume" aria-label="Elapsed time, tap to pause or resume">
<span class="timer-icon" aria-hidden="true">⏱</span><span id="timer-text">0:00</span>
</button>
<div class="quiz-progress">
<div class="progress-bar"><div class="progress-fill" id="progress-fill"></div></div>
<div class="progress-text" id="progress-text"></div>
</div>
</div>
</div>
<!-- Question navigator (jump to any question, see what's answered/flagged) -->
<div class="quiz-toolbar">
<button class="qnav-toggle" id="qnav-toggle" type="button" onclick="toggleNavigator()" aria-expanded="false" aria-controls="qnav-panel">
<span class="qnav-toggle-icon" aria-hidden="true">▦</span>
<span class="qnav-toggle-label">All questions</span>
<span class="qnav-stats" id="qnav-stats"></span>
<span class="qnav-caret" aria-hidden="true">▾</span>
</button>
<div class="qnav-panel" id="qnav-panel" hidden>
<div class="qnav-legend">
<span class="qnav-legend-item"><span class="qnav-swatch is-answered"></span>Answered</span>
<span class="qnav-legend-item"><span class="qnav-swatch is-flagged"></span>Flagged</span>
<span class="qnav-legend-item"><span class="qnav-swatch is-current"></span>Current</span>
<span class="qnav-legend-item"><span class="qnav-swatch"></span>Unanswered</span>
</div>
<div class="qnav-grid" id="qnav-grid"></div>
</div>
</div>
<div class="review-banner" id="review-banner" hidden></div>
<div class="question-card">
<div class="question-card-top">
<div id="question-type-badge" class="question-type-badge"></div>
<button class="flag-btn" id="flag-btn" type="button" onclick="toggleFlag()" aria-pressed="false">
<span class="flag-btn-icon" aria-hidden="true">🚩</span><span id="flag-label">Flag for review</span>
</button>
</div>
<div class="question-number" id="question-number"></div>
<div id="audio-container" class="audio-player" style="display:none">
<audio id="audio-el" controls preload="none"></audio>
<div class="audio-note" id="audio-note">Audio hosted by Mandarin Zone CDN · the real exam plays each clip once</div>
</div>
<div class="question-text chinese" id="question-text"></div>
<div id="question-image" hidden></div>
<div class="options-list" id="options-list"></div>
<div class="explanation-box" id="explanation-box" hidden></div>
</div>
<div class="nav-buttons">
<button class="btn btn-ghost" id="btn-quit" onclick="goHome()">← Back to Tests</button>
<div class="nav-buttons-group">
<button class="btn btn-secondary" id="btn-prev" onclick="prevQuestion()">Previous</button>
<button class="btn btn-primary" id="btn-next" onclick="nextQuestion()">Next →</button>
</div>
</div>
<p class="kbd-hint" id="kbd-hint">
<span class="kbd-hint-keys"><kbd>A</kbd>–<kbd>F</kbd> answer · <kbd>←</kbd><kbd>→</kbd> navigate · <kbd>F</kbd> flag</span>
</p>
</div>
<!-- RESULTS SCREEN -->
<div id="results-screen" class="screen">
<div class="results-card">
<div class="score-circle" id="score-circle">
<div class="score-num" id="score-num"></div>
<div class="score-label">correct</div>
</div>
<div class="results-msg" id="results-msg"></div>
<div class="results-detail" id="results-detail"></div>
<div class="results-breakdown">
<div class="breakdown-item"><div class="breakdown-num green" id="res-correct"></div><div class="breakdown-label">Correct</div></div>
<div class="breakdown-item"><div class="breakdown-num red" id="res-wrong"></div><div class="breakdown-label">Wrong</div></div>
<div class="breakdown-item"><div class="breakdown-num" id="res-unanswered"></div><div class="breakdown-label">Skipped</div></div>
<div class="breakdown-item"><div class="breakdown-num" id="res-time"></div><div class="breakdown-label">Time</div></div>
</div>
<!-- Per-section accuracy: tells the learner which skill to drill next -->
<div class="section-scores" id="section-scores"></div>
<div class="results-actions">
<button class="btn btn-secondary" id="btn-review-wrong" onclick="reviewWrongOnly()">Review wrong answers</button>
<button class="btn btn-secondary" onclick="reviewAnswers()">Review all</button>
<button class="btn btn-primary" onclick="retakeTest()">Retake test</button>
</div>
</div>
<!-- Smart next step: weak-section study links + next test -->
<div class="next-steps" id="next-steps"></div>
<div class="cta-banner">
<h3 class="chinese">准备好通过 HSK 4 了吗?</h3>
<p>98% HSK pass rate — Join 5,000+ students from 40+ countries · Trusted Chinese school since 2008</p>
<a href="https://mandarinzone.com" target="_blank" rel="noopener" class="btn btn-primary">Start Learning at Mandarin Zone</a>
<a href="https://www.mandarinzone.com/contact-us/" target="_blank" rel="noopener" class="cta-link">Have questions? Contact us →</a>
</div>
</div>
</main>
<footer>
<div class="footer-brand">
<a href="https://www.mandarinzone.com/" target="_blank" rel="noopener" class="footer-brand-link">
<img src="https://www.mandarinzone.com/wp-content/uploads/2015/01/logo.png" alt="Mandarin Zone" class="footer-logo" loading="lazy">
<div>
<div class="footer-brand-name">Mandarin Zone</div>
<div class="footer-tagline">Learn Chinese in Beijing & Online · Since 2008</div>
</div>
</a>
<div class="footer-cta">
<a href="https://www.mandarinzone.com/" target="_blank" rel="noopener" class="btn btn-ghost">Visit Website</a>
<a href="https://www.mandarinzone.com/contact-us/" target="_blank" rel="noopener" class="btn btn-ghost">Contact Us</a>
<a href="mailto:info@mandarinzone.com" class="btn btn-ghost">info@mandarinzone.com</a>
</div>
</div>
<p class="footer-links" style="margin-top:4px;"><a href="/vocabulary/">Vocabulary</a> · <a href="/grammar/">Grammar</a> · <a href="/writing/">Writing</a> · <a href="/words/">Confusable Words</a> · <a href="/guide/">Study Guide</a> · <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank" rel="noopener">CC BY-NC-SA 4.0</a> · <a href="https://github.com/Make-dream-clear/hsk4-mock-exam" target="_blank" rel="noopener">View on GitHub</a></p>
</footer>
<button id="to-top" class="to-top" type="button" aria-label="Back to top" hidden>
<svg viewBox="0 0 24 24" aria-hidden="true" width="20" height="20"><path d="M12 5l-7 7 1.41 1.41L11 8.83V19h2V8.83l4.59 4.58L19 12z" fill="currentColor"/></svg>
</button>
<script>
// === STATE ===
let allTests = [];
let currentTest = null;
let currentTestIndex = null;
let currentQ = 0; // absolute question index (0-based)
let userAnswers = {}; // qIndex -> selected option index
let flags = {}; // qIndex -> true (marked for review)
let reviewMode = false;
let reviewList = null; // null = walk all questions; array = subset (wrong-only)
let elapsed = 0; // seconds spent on the current attempt
let timerId = null;
let timerPaused = false;
let lastResult = null; // cached result for review/next-steps
const STORE = 'hsk4_progress_'; // in-progress attempt, per test
const STORE_RESULT = 'hsk4_result_'; // last completed score, per test
// === SECTIONS ===
// Map each question type to an exam section so we can score by skill.
const SECTION_OF = {
listening_true_false: 'Listening',
listening_choice: 'Listening',
fill_in_blank: 'Reading',
reading_ordering: 'Reading',
reading_comprehension: 'Reading',
choice: 'Writing',
writing_construction: 'Writing',
};
const SECTION_META = {
Listening: { zh: '听力', cls: 'badge-listening', href: '/strategies/listening-judgment/', resource: 'Listening strategies' },
Reading: { zh: '阅读', cls: 'badge-reading', href: '/strategies/reading-comprehension/', resource: 'Reading strategies' },
Writing: { zh: '书写', cls: 'badge-writing', href: '/sentences/', resource: '100 essential sentences' },
};
function sectionOf(q) { return SECTION_OF[q.type] || 'Reading'; }
const TYPE_MAP = {
listening_true_false: ['Listening · 听力判断', 'badge-listening'],
listening_choice: ['Listening · 听力选择', 'badge-listening'],
fill_in_blank: ['Reading · 选词填空', 'badge-reading'],
reading_ordering: ['Reading · 语句排序', 'badge-reading'],
reading_comprehension: ['Reading · 阅读理解', 'badge-reading'],
choice: ['Writing · 书写', 'badge-writing'],
writing_construction: ['Writing · 看图造句', 'badge-writing'],
};
const MARKERS = ['A', 'B', 'C', 'D', 'E', 'F'];
// === UTILS ===
function escHtml(str) {
const d = document.createElement('div');
d.textContent = str == null ? '' : str;
return d.innerHTML;
}
function pad2(n) { return String(n).padStart(2, '0'); }
function fmtTime(s) {
s = Math.max(0, Math.floor(s));
const h = Math.floor(s / 3600), m = Math.floor((s % 3600) / 60), sec = s % 60;
return (h > 0 ? h + ':' + pad2(m) : m) + ':' + pad2(sec);
}
function $(id) { return document.getElementById(id); }
// === PERSISTENCE (localStorage; degrades silently in private mode) ===
function progKey(i) { return STORE + i; }
function resultKey(i) { return STORE_RESULT + i; }
function saveProgress() {
if (currentTestIndex == null || reviewMode) return;
try {
localStorage.setItem(progKey(currentTestIndex), JSON.stringify({
answers: userAnswers, flags: flags, currentQ: currentQ, elapsed: elapsed, ts: Date.now(),
}));
} catch (e) {}
}
function loadProgress(i) {
try { const raw = localStorage.getItem(progKey(i)); return raw ? JSON.parse(raw) : null; }
catch (e) { return null; }
}
function clearProgress(i) { try { localStorage.removeItem(progKey(i)); } catch (e) {} }
function saveResult(i, r) { try { localStorage.setItem(resultKey(i), JSON.stringify(r)); } catch (e) {} }
function loadResult(i) {
try { const raw = localStorage.getItem(resultKey(i)); return raw ? JSON.parse(raw) : null; }
catch (e) { return null; }
}
function answeredCount(answers) { return answers ? Object.keys(answers).length : 0; }
// === DATA LOADING ===
const BASE = './data/';
async function loadIndex() {
try {
const res = await fetch(BASE + 'index.json');
if (!res.ok) throw new Error('HTTP ' + res.status);
allTests = await res.json();
renderTestGrid();
handleDeepLink();
} catch (e) {
$('test-grid').innerHTML = '<p style="color:var(--stone);padding:20px;">Failed to load tests. Make sure you\'re serving this from a web server or GitHub Pages.</p>';
}
}
// Auto-start a test from /?start=N (used by every static test page's
// "Start Interactive Test" button). Clean the URL afterwards so a refresh
// doesn't relaunch the quiz unexpectedly.
function handleDeepLink() {
const params = new URLSearchParams(window.location.search);
if (!params.has('start')) return;
const n = parseInt(params.get('start'), 10);
try { history.replaceState(null, '', window.location.pathname + window.location.hash); } catch (e) {}
if (!isNaN(n) && n >= 0 && n < allTests.length) startTest(n);
}
function renderTestGrid() {
const grid = $('test-grid');
grid.innerHTML = allTests.map((t, i) => {
const prog = loadProgress(i);
const done = answeredCount(prog && prog.answers);
const result = loadResult(i);
let status = '';
if (done > 0) {
status = `<span class="test-card-status is-progress">In progress · ${done}/${t.questions}</span>`;
} else if (result) {
status = `<span class="test-card-status ${result.pct >= 60 ? 'is-pass' : 'is-fail'}">Last score ${result.pct}%</span>`;
}
return `
<div class="test-card" tabindex="0" role="button" onclick="startTest(${i})" onkeydown="if(event.key==='Enter'||event.key===' '){event.preventDefault();startTest(${i})}">
<div class="test-card-num">Test ${pad2(i + 1)}</div>
<div class="test-card-title">${escHtml(t.title)}</div>
<div class="test-card-meta">
<span>${t.questions} questions</span>
<span>~50 min</span>
</div>
${status}
</div>`;
}).join('');
}
// === SCREENS ===
function showScreen(id) {
document.querySelectorAll('.screen').forEach(s => s.classList.remove('active'));
$(id).classList.add('active');
window.scrollTo(0, 0);
}
function goHome() {
stopTimer();
saveProgress();
currentTest = null;
currentTestIndex = null;
currentQ = 0;
userAnswers = {};
flags = {};
reviewMode = false;
reviewList = null;
closeNavigator();
renderTestGrid();
showScreen('home-screen');
}
// === QUIZ LIFECYCLE ===
async function startTest(index) {
const meta = allTests[index];
if (!meta) return;
try {
const res = await fetch(BASE + meta.file);
if (!res.ok) throw new Error('HTTP ' + res.status);
currentTest = await res.json();
currentTestIndex = index;
} catch (e) {
alert('Failed to load test data.');
return;
}
const saved = loadProgress(index);
if (saved && answeredCount(saved.answers) > 0) {
showResumePrompt(index, saved);
} else {
beginAttempt(null);
}
}
function beginAttempt(saved) {
reviewMode = false;
reviewList = null;
if (saved) {
userAnswers = saved.answers || {};
flags = saved.flags || {};
currentQ = saved.currentQ || 0;
elapsed = saved.elapsed || 0;
} else {
userAnswers = {};
flags = {};
currentQ = 0;
elapsed = 0;
if (currentTestIndex != null) clearProgress(currentTestIndex);
}
$('review-banner').hidden = true;
$('kbd-hint').style.display = '';
closeNavigator();
showScreen('quiz-screen');
startTimer();
renderQuestion();
}
// Lightweight resume dialog, built on the fly so the static markup stays clean.
function showResumePrompt(index, saved) {
const total = currentTest.questions.length;
const done = answeredCount(saved.answers);
const overlay = document.createElement('div');
overlay.className = 'modal-overlay';
overlay.innerHTML = `
<div class="modal-card" role="dialog" aria-modal="true" aria-labelledby="resume-title">
<h3 id="resume-title">Resume Test ${pad2(index + 1)}?</h3>
<p>You answered <strong>${done}/${total}</strong> questions last time (${fmtTime(saved.elapsed || 0)} on the clock). Pick up where you left off, or start over?</p>
<div class="modal-actions">
<button class="btn btn-ghost" data-act="restart">Start over</button>
<button class="btn btn-primary" data-act="resume">Resume</button>
</div>
</div>`;
document.body.appendChild(overlay);
const close = (resume) => { overlay.remove(); beginAttempt(resume ? saved : null); };
overlay.addEventListener('click', (e) => {
const act = e.target.getAttribute && e.target.getAttribute('data-act');
if (act === 'resume') close(true);
else if (act === 'restart') close(false);
});
const resumeBtn = overlay.querySelector('[data-act="resume"]');
if (resumeBtn) resumeBtn.focus();
}
// Questions to walk for prev/next (all, or the wrong-only subset in review).
function navList() {
if (reviewMode && reviewList) return reviewList;
return currentTest.questions.map((_, i) => i);
}
function renderQuestion() {
const q = currentTest.questions[currentQ];
const list = navList();
const pos = list.indexOf(currentQ);
$('quiz-title').textContent = currentTest.title;
$('progress-fill').style.width = ((pos + 1) / list.length * 100) + '%';
$('progress-text').textContent = `${pos + 1} / ${list.length}`;
$('question-number').textContent = `Question ${q.number}`;
// Type badge
const badge = $('question-type-badge');
const [label, cls] = TYPE_MAP[q.type] || ['Question', 'badge-reading'];
badge.textContent = label;
badge.className = 'question-type-badge ' + cls;
// Flag button (hidden in review)
const flagBtn = $('flag-btn');
const flagged = !!flags[currentQ];
flagBtn.style.display = reviewMode ? 'none' : '';
flagBtn.classList.toggle('is-flagged', flagged);
flagBtn.setAttribute('aria-pressed', flagged ? 'true' : 'false');
$('flag-label').textContent = flagged ? 'Flagged' : 'Flag for review';
// Audio
const ac = $('audio-container');
const ae = $('audio-el');
const an = $('audio-note');
const isListening = q.type && q.type.startsWith('listening');
if (q.audio) {
// Per-question clip (Mandarin Zone tests).
ac.style.display = 'block';
if (ae.getAttribute('src') !== q.audio) ae.src = q.audio;
an.textContent = 'Audio hosted by Mandarin Zone CDN · the real exam plays each clip once';
} else if (currentTest.listening_audio && isListening) {
// Official papers: one continuous track for the whole listening section.
// Only (re)set the src when it changes so navigating between listening
// questions doesn't restart the audio.
ac.style.display = 'block';
// listening_audio is a root-relative path (e.g. /test/13/listening.mp3).
if (ae.getAttribute('src') !== currentTest.listening_audio) ae.src = currentTest.listening_audio;
an.textContent = 'Official exam audio · one continuous track for the whole listening section (plays once in the real exam)';
} else {
ac.style.display = 'none';
if (ae.getAttribute('src')) { ae.pause(); ae.removeAttribute('src'); }
}
// Text
$('question-text').textContent = q.text || '';
// Image (看图造句 writing prompts)
const qi = $('question-image');
if (q.image) {
qi.hidden = false;
qi.innerHTML = '<img src="' + escHtml(q.image) + '" alt="HSK 4 看图造句 writing prompt" loading="lazy">';
} else {
qi.hidden = true;
qi.innerHTML = '';
}
// Options
const ol = $('options-list');
const selected = userAnswers[currentQ];
ol.innerHTML = q.options.map((opt, i) => {
let oc = 'option-btn';
if (reviewMode) {
oc += ' disabled';
if (i === q.correct_answer_index) oc += ' correct';
else if (i === selected && i !== q.correct_answer_index) oc += ' wrong';
} else if (i === selected) {
oc += ' selected';
}
return `<button class="${oc}" onclick="selectOption(${i})">
<span class="marker">${MARKERS[i] || i + 1}</span>
<span class="chinese">${escHtml(opt)}</span>
</button>`;
}).join('');
// Explanation (review only — surfaces the answer key + any explanation note)
const exBox = $('explanation-box');
if (reviewMode) {
const cm = MARKERS[q.correct_answer_index] || (q.correct_answer_index + 1);
const ua = userAnswers[currentQ];
let you;
if (ua === undefined) you = `<div class="ex-you ex-skip">You skipped this question.</div>`;
else if (ua === q.correct_answer_index) you = `<div class="ex-you ex-correct">✓ You answered correctly.</div>`;
else you = `<div class="ex-you ex-wrong">✗ You chose ${MARKERS[ua] || ua + 1}. ${escHtml(q.options[ua])}</div>`;
exBox.innerHTML =
`<div class="ex-answer">Correct answer: <strong>${cm}. <span class="chinese">${escHtml(q.options[q.correct_answer_index])}</span></strong></div>` +
you +
(q.note ? `<div class="ex-explain">${escHtml(q.note)}</div>` : '') +
(q.explanation ? `<div class="ex-explain"><span class="ex-explain-label">解析 · Explanation</span>${escHtml(q.explanation)}</div>` : '') +
(q.transcript ? `<div class="ex-explain"><span class="ex-explain-label">听力原文 · Transcript</span><span class="chinese" style="white-space:pre-wrap">${escHtml(q.transcript)}</span></div>` : '');
exBox.hidden = false;
} else {