纯CSS菜单样式,及其Shadow DOM,Json接口 实现
先声明,要看懂这篇博客要求你具备少量基础CSS知识,
当然如果你只是要用的话就随便了,不用了解任何知识
先放一张效果图
Part 1:纯CSS菜单样式
先放样式代码
1 <style>
2 *:focus{outline:none}
3 menu{
4 display:none;
5 position:absolute;
6 margin-top:0;
7 top:0;
8 margin-left:0;
9 left:0;
10 margin-right:0;
11 right:0;
12 height:21px;
13 padding-left:1px;
14 white-space:nowrap;
15 background-color:#f0f0f0;
16 user-select:none;
17 cursor:default;
18 }
19 menu item{
20 display:inline-block;
21 padding:3px 6px 2px 6px;
22 font-size:12px;
23 vertical-align:top;
24 }
25 menu submenu{
26 position:absolute;
27 display:none;
28 width:auto;
29 margin-left:-6px;
30 background-color:#fff;
31 font-size:16px;
32 box-shadow:1px 1px 16px #aaa;
33 }
34 menu item:hover{background-color:#ddd}
35 menu:focus item:hover submenu{display:block}
36 menu delims{display:inline;position:absolute;margin:-2px 2px}
37 menu submenu item{display:block;width:auto}
38 menu submenu label{display:block;margin:-4px 4px}
39 menu submenu hr{display:inline-block;width:100%;margin:0}
40 menu input{display:inline-block;width:0}
41 menu span{display:inline-block;margin-left:-6px}
42 menu span + span{margin-left:6px}
43 menu submenu span + span{padding:6px}
44 menu input + span{width:9px}
45 menu input[used]:checked + span::before{content:"\2713"}
46 menu label::after{
47 float:right;
48 margin: 0 8px 0 16px;
49 padding:5px;
50 color:gray;
51 content:attr(description);
52 }
53 menu label:hover::after{color:#000}
54 :host(.show) menu{display:block}
55 :host(.night) menu{color:#eee;background-color:#2d2d30}
56 :host(.night) menu item:hover{background-color:#555}
57 :host(.night) menu delims{color:#aaa}
58 :host(.night) menu submenu{background-color:#2d2d30;box-shadow:1px 1px 16px #fff}
59 :host(.night) menu label::after{color:#fff}
60 menu span + span + span{display:none}
61 :host(.EN) menu span + span{display:none}
62 :host(.EN) menu span + span + span{display:inline-block}
63 :host(.EN) menu label:after{content:attr(EN-description)}
64 :host(.english) menu span + span{display:none}
65 :host(.english) menu span + span + span{display:inline-block}
66 :host(.english) menu label:after{content:attr(EN-description)}
67 </style>
68
69 <menu tabindex="0" oncontextmenu="return false">
70 <item style="">
71 <span></span>
72 <span>文件(F)</span>
73 <span>File(F)</span>
74 <submenu>
75 <item style="" onmouseup="">
76 <label description="Ctrl+N" en-description="Ctrl+N">
77 <input type="checkbox">
78 <span></span>
79 <span>新建文件</span>
80 <span>New File</span>
81 </label>
82 </item>
83 <item style="" onmouseup="{}">
84 <label description="Alt+N" en-description="Alt+N">
85 <input type="checkbox">
86 <span></span>
87 <span>新建项目</span>
88 <span>New Project</span>
89 </label>
90 </item>
91 <hr>
92 <item style="" onmouseup="{}">
93 <label description="Ctrl+O" en-description="Ctrl+O">
94 <input type="checkbox">
95 <span></span>
96 <span>打开文件</span>
97 <span>Open File</span>
98 </label>
99 </item>
100 <item style="" onmouseup="{}">
101 <label description="Alt+O" en-description="Alt+O">
102 <input type="checkbox">
103 <span></span>
104 <span>打开文件夹</span>
105 <span>Open Folder</span>
106 </label>
107 </item>
108 <item style="" onmouseup="{}">
109 <label description="Shift+O" en-description="Shift+O">
110 <input type="checkbox">
111 <span></span>
112 <span>导入云端项目</span>
113 <span>Open Cloud Project</span>
114 </label>
115 </item>
116 <item style="" onmouseup="{}">
117 <label description="Shift+G" en-description="Shift+G">
118 <input type="checkbox">
119 <span></span>
120 <span>导入github项目</span>
121 <span>Open Github Project</span>
122 </label>
123 </item>
124 <item style="" onmouseup="{}">
125 <label description="Shift+Alt+O" en-description="Shift+Alt+O">
126 <input type="checkbox">
127 <span></span>
128 <span>导入其他云项目</span>
129 <span>Open Other Cloud Project</span>
130 </label>
131 </item>
132 <hr>
133 <item style="" onmouseup="{}">
134 <label description="Ctrl+S" en-description="Ctrl+S">
135 <input type="checkbox">
136 <span></span>
137 <span>本地保存</span>
138 <span>Save Locally</span>
139 </label>
140 </item>
141 <item style="" onmouseup="{}">
142 <label description="Shift+S" en-description="Shift+S">
143 <input type="checkbox">
144 <span></span>
145 <span>云端保存</span>
146 <span>Save to Cloud</span>
147 </label>
148 </item>
149 <item style="" onmouseup="{}">
150 <label description="Shift+Alt+S" en-description="Shift+Alt+S">
151 <input type="checkbox">
152 <span></span>
153 <span>保存为模板</span>
154 <span>Save As Template</span>
155 </label>
156 </item>
157 <hr>
158 <item style="" onmouseup="{}">
159 <label description="即时云端缓存代码" en-description="Instant cloud caching code">
160 <input type="checkbox" used="">
161 <span></span>
162 <span>禁用数据同步</span>
163 <span>forbidden DS</span>
164 </label>
165 </item>
166 <hr>
167 <item style="" onmouseup="{}">
168 <label description="Alt+S" en-description="Alt+S">
169 <input type="checkbox">
170 <span></span>
171 <span>设置</span>
172 <span>Settings</span>
173 </label>
174 </item>
175 <hr>
176 <item style="" onmouseup="{}">
177 <label description="Shift+Z" en-description="Shift+Z">
178 <input type="checkbox">
179 <span></span>
180 <span>还原云端文件</span>
181 <span>Recovery File From Cloud</span>
182 </label>
183 </item>
184 <item style="" onmouseup="{}">
185 <label description="Ctrl+F4" en-description="Ctrl+F4">
186 <input type="checkbox">
187 <span></span>
188 <span>关闭文件</span>
189 <span>Close File</span>
190 </label>
191 </item>
192 <item style="" onmouseup="{}">
193 <label description="Ctrl+Shift+F4" en-description="Ctrl+Shift+F4">
194 <input type="checkbox">
195 <span></span>
196 <span>关闭文件夹</span>
197 <span>Close Folder</span>
198 </label>
199 </item>
200 <item style="" onmouseup="{}">
201 <label description="Ctrl+Alt+F4" en-description="Ctrl+Alt+F4">
202 <input type="checkbox">
203 <span></span>
204 <span>关闭项目</span>
205 <span>Close Project</span>
206 </label>
207 </item>
208 <hr>
209 <item style="" onmouseup="{}">
210 <label description="Shift+Alt+F4" en-description="Shift+Alt+F4">
211 <input type="checkbox">
212 <span></span>
213 <span>退出</span>
214 <span>Exit</span>
215 </label>
216 </item>
217 </submenu>
218 </item>
219 <item style="">
220 <span></span>
221 <span>编辑(E)</span>
222 <span>Edit(E)</span>
223 <submenu>
224 <item style="" onmouseup="{}">
225 <label description="Ctrl+Z" en-description="Ctrl+Z">
226 <input type="checkbox">
227 <span></span>
228 <span>撤销</span>
229 <span>Undo</span>
230 </label>
231 </item>
232 <item style="" onmouseup="{}">
233 <label description="Ctrl+Y" en-description="Ctrl+Y">
234 <input type="checkbox">
235 <span></span>
236 <span>重做</span>
237 <span>Redo</span>
238 </label>
239 </item>
240 <hr>
241 <item style="" onmouseup="{}">
242 <label description="Ctrl+X" en-description="Ctrl+X">
243 <input type="checkbox">
244 <span></span>
245 <span>剪切</span>
246 <span>Cut</span>
247 </label>
248 </item>
249 <item style="" onmouseup="{}">
250 <label description="Ctrl+C" en-description="Ctrl+C">
251 <input type="checkbox">
252 <span></span>
253 <span>复制</span>
254 <span>Copy</span>
255 </label>
256 </item>
257 <item style="" onmouseup="{}">
258 <label description="Ctrl+V" en-description="Ctrl+V">
259 <input type="checkbox">
260 <span></span>
261 <span>粘贴</span>
262 <span>Paste</span>
263 </label>
264 </item>
265 <hr>
266 <item style="" onmouseup="{}">
267 <label description="Ctrl+A" en-description="Ctrl+A">
268 <input type="checkbox">
269 <span></span>
270 <span>全选</span>
271 <span>Select All</span>
272 </label>
273 </item>
274 <hr>
275 <item style="" onmouseup="{}">
276 <label description="" en-description="">
277 <input type="checkbox">
278 <span></span>
279 <span>"Ctrl+单击"跳转到定义</span>
280 <span>"Ctrl+Click"Jump to definition</span>
281 </label>
282 </item>
283 <item style="" onmouseup="{}">
284 <label description="" en-description="">
285 <input type="checkbox" used="">
286 <span></span>
287 <span>"Alt+单击"进行多光标功能</span>
288 <span>"Ctrl+Click"Multiple Cursors For Edit</span>
289 </label>
290 </item>
291 <hr>
292 <item style="" onmouseup="{}">
293 <label description="Ctrl+F" en-description="Ctrl+F">
294 <input type="checkbox">
295 <span></span>
296 <span>查找</span>
297 <span>Find</span>
298 </label>
299 </item>
300 <item style="" onmouseup="{}">
301 <label description="Ctrl+H" en-description="Ctrl+H">
302 <input type="checkbox">
303 <span></span>
304 <span>替换</span>
305 <span>Replace</span>
306 </label>
307 </item>
308 <hr>
309 <item style="" onmouseup="{}">
310 <label description="Ctrl+Shift+F" en-description="Ctrl+Shift+F">
311 <input type="checkbox">
312 <span></span>
313 <span>打开文件中查找</span>
314 <span>Find in Opened Files</span>
315 </label>
316 </item>
317 <item style="" onmouseup="{}">
318 <label description="Ctrl+Shift+H" en-description="Ctrl+Shift+H">
319 <input type="checkbox">
320 <span></span>
321 <span>打开文件中替换</span>
322 <span>Replace in Opened Files</span>
323 </label>
324 </item>
325 <item style="" onmouseup="{}">
326 <label description="Ctrl+Alt+F" en-description="Ctrl+Alt+F">
327 <input type="checkbox">
328 <span></span>
329 <span>所有文件中查找</span>
330 <span>Find in All Files</span>
331 </label>
332 </item>
333 <item style="" onmouseup="{}">
334 <label description="Ctrl+Alt+H" en-description="Ctrl+Alt+H">
335 <input type="checkbox">
336 <span></span>
337 <span>所有文件中替换</span>
338 <span>Replace in All Files</span>
339 </label>
340 </item>
341 <hr>
342 <item style="" onmouseup="{}">
343 <label description="Ctrl+/" en-description="Ctrl+/">
344 <input type="checkbox">
345 <span></span>
346 <span>切换行注释</span>
347 <span>Toggle line Comment</span>
348 </label>
349 </item>
350 <item style="" onmouseup="{}">
351 <label description="Ctrl+." en-description="Ctrl+.">
352 <input type="checkbox">
353 <span></span>
354 <span>切换块注释</span>
355 <span>Toggle Block Comment</span>
356 </label>
357 </item>
358 <hr>
359 <item style="" onmouseup="{}">
360 <label description="Ctrl+M" en-description="Ctrl+M">
361 <input type="checkbox">
362 <span></span>
363 <span>代码格式化</span>
364 <span>Code Formatter</span>
365 </label>
366 </item>
367 <item style="" onmouseup="{}">
368 <label description="Ctrl+J" en-description="Ctrl+J">
369 <input type="checkbox">
370 <span></span>
371 <span>全部折叠</span>
372 <span>Collapse All</span>
373 </label>
374 </item>
375 <item style="" onmouseup="{}">
376 <label description="Ctrl+K" en-description="Ctrl+K">
377 <input type="checkbox">
378 <span></span>
379 <span>全部展开</span>
380 <span>Uncollapse All</span>
381 </label>
382 </item>
383 </submenu>
384 </item>
385 <item style="">
386 <span></span>
387 <span>视图(V)</span>
388 <span>View(V)</span>
389 <submenu>
390 <item style="" onmouseup="{}">
391 <label description="Alt+C" en-description="Alt+C">
392 <input type="checkbox">
393 <span></span>
394 <span>命令面板</span>
395 <span>Command Panel</span>
396 </label>
397 </item>
398 <item style="" onmouseup="{}">
399 <label description="Alt+X" en-description="Alt+X">
400 <input type="checkbox">
401 <span></span>
402 <span>打开视图</span>
403 <span>View Panel</span>
404 </label>
405 </item>
406 <hr>
407 <item style="" onmouseup="{}">
408 <label description="F3" en-description="F3">
409 <input type="checkbox">
410 <span></span>
411 <span>搜索文件</span>
412 <span>Search File</span>
413 </label>
414 </item>
415 <item style="" onmouseup="{}">
416 <label description="Shift+T" en-description="Shift+T">
417 <input type="checkbox">
418 <span></span>
419 <span>源代码管理</span>
420 <span>Source Code Manager</span>
421 </label>
422 </item>
423 <item style="" onmouseup="{}">
424 <label description="Alt+T" en-description="Alt+T">
425 <input type="checkbox">
426 <span></span>
427 <span>资源管理器</span>
428 <span>Resource Manager</span>
429 </label>
430 </item>
431 <item style="" onmouseup="{}">
432 <label description="Alt+Y" en-description="Alt+Y">
433 <input type="checkbox">
434 <span></span>
435 <span>代码定义</span>
436 <span>Code definition Show</span>
437 </label>
438 </item>
439 <item style="" onmouseup="{}">
440 <label description="Alt+U" en-description="Alt+U">
441 <input type="checkbox">
442 <span></span>
443 <span>调试面板</span>
444 <span>Debug Panel</span>
445 </label>
446 </item>
447 <item style="" onmouseup="{}">
448 <label description="F12" en-description="F12">
449 <input type="checkbox">
450 <span></span>
451 <span>拓展</span>
452 <span>Extension</span>
453 </label>
454 </item>
455 <hr>
456 <item style="" onmouseup="{}">
457 <label description="Alt+J" en-description="Alt+J">
458 <input type="checkbox">
459 <span></span>
460 <span>输出</span>
461 <span>Output</span>
462 </label>
463 </item>
464 <item style="" onmouseup="{}">
465 <label description="Alt+K" en-description="Alt+K">
466 <input type="checkbox">
467 <span></span>
468 <span>问题</span>
469 <span>Problem</span>
470 </label>
471 </item>
472 <item style="" onmouseup="{}">
473 <label description="Alt+L" en-description="Alt+L">
474 <input type="checkbox">
475 <span></span>
476 <span>调试控制台</span>
477 <span>Debug Console</span>
478 </label>
479 </item>
480 <item style="" onmouseup="{}">
481 <label description="Alt+`" en-description="Alt+`">
482 <input type="checkbox">
483 <span></span>
484 <span>集成终端</span>
485 <span>Integrated Terminal</span>
486 </label>
487 </item>
488 <hr>
489 <item style="" onmouseup="{}">
490 <label description="F11" en-description="F11">
491 <input type="checkbox">
492 <span></span>
493 <span>切换全屏</span>
494 <span>Toggle Fullscreen</span>
495 </label>
496 </item>
497 <item style="" onmouseup="{}">
498 <label description="Alt+Shift+M" en-description="Alt+Shift+M">
499 <input type="checkbox">
500 <span></span>
501 <span>切换菜单栏</span>
502 <span>Toggle Menu</span>
503 </label>
504 </item>
505 <hr>
506 <item style="" onmouseup="{}">
507 <label description="Alt+Shift+Z" en-description="Alt+Shift+Z">
508 <input type="checkbox">
509 <span></span>
510 <span>切换活动栏</span>
511 <span>Toggle Activity Panel</span>
512 </label>
513 </item>
514 <item style="" onmouseup="{}">
515 <label description="Alt+Shift+X" en-description="Alt+Shift+X">
516 <input type="checkbox">
517 <span></span>
518 <span>切换侧边栏</span>
519 <span>Toggle Aside Panel</span>
520 </label>
521 </item>
522 <item style="" onmouseup="{}">
523 <label description="Alt+Shift+C" en-description="Alt+Shift+C">
524 <input type="checkbox">
525 <span></span>
526 <span>切换侧边栏左右位置</span>
527 <span>Toggle Aside Panel Left Or Right</span>
528 </label>
529 </item>
530 <item style="" onmouseup="{}">
531 <label description="Alt+Shift+V" en-description="Alt+Shift+V">
532 <input type="checkbox">
533 <span></span>
534 <span>切换输出控制台</span>
535 <span>Toggle Output Console</span>
536 </label>
537 </item>
538 <item style="" onmouseup="{}">
539 <label description="Alt+Shift+B" en-description="Alt+Shift+B">
540 <input type="checkbox">
541 <span></span>
542 <span>切换状态栏</span>
543 <span>Toggle Status Bar</span>
544 </label>
545 </item>
546 <hr>
547 <item style="" onmouseup="{}">
548 <label description="Alt+\" en-description="Alt+\">
549 <input type="checkbox">
550 <span></span>
551 <span>拆分编辑器</span>
552 <span>Split Editor</span>
553 </label>
554 </item>
555 <item style="" onmouseup="{}">
556 <label description="Alt+Q" en-description="Alt+Q">
557 <input type="checkbox">
558 <span></span>
559 <span>切换小窗口</span>
560 <span>Toggle Small Window</span>
561 </label>
562 </item>
563 </submenu>
564 </item>
565 <item style="">
566 <span></span>
567 <span>运行(R)</span>
568 <span>Run(R)</span>
569 <submenu>
570 <item style="" onmouseup="{}">
571 <label description="F8" en-description="F8">
572 <input type="checkbox">
573 <span></span>
574 <span>编译</span>
575 <span>Compile</span>
576 </label>
577 </item>
578 <item style="" onmouseup="{}">
579 <label description="F9" en-description="F9">
580 <input type="checkbox">
581 <span></span>
582 <span>运行</span>
583 <span>Run</span>
584 </label>
585 </item>
586 <item style="" onmouseup="{}">
587 <label description="F10" en-description="F10">
588 <input type="checkbox">
589 <span></span>
590 <span>编译运行</span>
591 <span>Compile And Run</span>
592 </label>
593 </item>
594 <hr>
595 <item style="" onmouseup="{}">
596 <label description="Alt+A" en-description="Alt+A">
597 <input type="checkbox">
598 <span></span>
599 <span>性能分析</span>
600 <span>Performance analysis</span>
601 </label>
602 </item>
603 <hr>
604 <item style="" onmouseup="{}">
605 <label description="F5" en-description="F5">
606 <input type="checkbox">
607 <span></span>
608 <span>调试</span>
609 <span>Debug</span>
610 </label>
611 </item>
612 <item style="" onmouseup="{}">
613 <label description="F6" en-description="F6">
614 <input type="checkbox">
615 <span></span>
616 <span>停止调试</span>
617 <span>Stop Debug</span>
618 </label>
619 </item>
620 <item style="" onmouseup="{}">
621 <label description="F7" en-description="F7">
622 <input type="checkbox">
623 <span></span>
624 <span>添加断点</span>
625 <span>Add Breakpoint</span>
626 </label>
627 </item>
628 <item style="" onmouseup="{}">
629 <label description="Shift+Q" en-description="Shift+Q">
630 <input type="checkbox">
631 <span></span>
632 <span>下一条语句</span>
633 <span>Next Command</span>
634 </label>
635 </item>
636 <item style="" onmouseup="{}">
637 <label description="Shift+W" en-description="Shift+W">
638 <input type="checkbox">
639 <span></span>
640 <span>下一行</span>
641 <span>Next Line</span>
642 </label>
643 </item>
644 <item style="" onmouseup="{}">
645 <label description="Shift+E" en-description="Shift+E">
646 <input type="checkbox" used="">
647 <span></span>
648 <span>继续</span>
649 <span>Continue</span>
650 </label>
651 </item>
652 <hr>
653 <item style="" onmouseup="{}">
654 <label description="Shift+\" en-description="Shift+\">
655 <input type="checkbox">
656 <span></span>
657 <span>提交调试信息</span>
658 <span>Push debug information</span>
659 </label>
660 </item>
661 </submenu>
662 </item>
663 <item style="">
664 <span></span>
665 <span>帮助(H)</span>
666 <span>Help(H)</span>
667 <submenu>
668 <item style="" onmouseup="{}">
669 <label description="F1" en-description="F1">
670 <input type="checkbox">
671 <span></span>
672 <span>交互教程</span>
673 <span>Interactive Tutorials</span>
674 </label>
675 </item>
676 <item style="" onmouseup="{}">
677 <label description="Alt+D" en-description="Alt+D">
678 <input type="checkbox">
679 <span></span>
680 <span>说明文档</span>
681 <span>Description document</span>
682 </label>
683 </item>
684 <item style="" onmouseup="{}">
685 <label description="Alt+B" en-description="Alt+B">
686 <input type="checkbox">
687 <span></span>
688 <span>发行说明</span>
689 <span>Issuance Instructions</span>
690 </label>
691 </item>
692 <hr>
693 <item style="" onmouseup="{}">
694 <label description="Alt+/" en-description="Alt+/">
695 <input type="checkbox">
696 <span></span>
697 <span>关于</span>
698 <span>About</span>
699 </label>
700 </item>
701 </submenu>
702 </item>
703 </menu>
忽然发现这个并不是纯CSS版本,这是我封装后生成的代码,因为我没有保存,又懒得再打,就用这个讲吧
不过我会分析代码,相信你认真看完完全可以自己打一个纯CSS版本的菜单
当然,如果你复制粘贴运行,会发现没有显示,哈哈,只要令menu的display为block即可,至于为什么要不显示,是我开发中需要这个拓展,就不提了;
纯css版本是怎么运行的呢,无非利用Focus,hover控制menu显示与否,
既如下代码
<style> Submenu{display:none} item:hover Submenu{display:block} </style> <menu> <item> 菜单项 <submenu>子菜单项</submenu> </item> </menu>
显然就是把鼠标放在菜单项上时显示子菜单
好吧,别着急,别着急,我知道这些你都知道了,那我们就讲讲菜单要什么功能,及其关键代码吧。
首先,肯定不能像这样鼠标放上去就显示子菜单,(有些人喜欢自动显示的,但是,我们这里讲普通菜单),既点击后显示。
然后点击一个菜单项,显示其子菜单,移到另外一个菜单项时,不用点击就自动显示其子菜单,原来的子菜单关闭
菜单可能包括check属性,
(我承认,我这里漏了一些东西,比如鼠标离开菜单,最后一个子菜单应该保持显示,还有为什么我的菜单还可以换语言和颜色,我们先不提,的确有些事我们没有做到,但都是不影响使用的,或者需要之后ShadowDOM知识的)
首先是,点击菜单,显示,点击子菜单项或其他东西,隐藏,
第一种方法
我们在其外面加一个label ,旁边加一个checkbox,
<label><input type="checkbox">###menu###</label>
这样我们就可以通过点击菜单,控制input的check属性,再利用CSS的属性选择器"[ ]"和兄弟选择器"+"即可控制子菜单的显示
<style> input[check] + menu > submenu{display:block} </style>
,另外还要隐藏checkbox,这是网上常见的写法
不过,你觉不觉得好麻烦,嗯,我也这样觉得,所以我们还有另外的写法,就是利用Focus,
“什么?不是只有form元素像input才能获得焦点吗”,如果你对html一知半解或者缺乏开发经验你会这样想,
但是我们只要利用一个参数 tabindex即可
对,就是这个参数,原来用来控制Tab导航顺序,事实上,只要赋值0,就可以让任何元素具备被聚焦的能力,尝试运行以下代码
<style> menu submenu{display:none} menu:focus item:hover submenu{display:block} </style> <menu tabindex="0"> <item> 菜单项1 <submenu>子菜单</submenu> </item> <item> 菜单项2 <submenu>子菜单</submenu> </item> </menu>
发现已经做到点击显示,随便还实现了移动到其余菜单项自动显示子菜单的功能
不过focus到的元素都有outline,只要menu {outline:none}即可,
另外位置不对,因为他们属于同一个流,要脱离文档流,submenu {position:absolute}
“等等,你又说漏一个” ,什么你又发现了?好吧,点击子菜单项之后菜单没有消失。我承认我现在没有办法,
“什么呀。。原来白看这么久吗?”, 等等,别走,再看一句,我下面就有办法了。先实现下面一个功能。
菜单check属性,也很简单,记得我们第一种实现点击显示的方法吗
我们只要让它们display为inline-block,在让他们处于同一个div层,就成为一个带checkbox的菜单项不过。。。这也太丑了吧
快让它消失,让它成为可设置宽度的inline-block再把宽度设为0
menu input{display:inline-block;width:0}
好的它不见了
我们的样式
menu input[used]:checked + span::before{content:"\2713"}
嗯?这个used是什么,废话,不是所有的菜单项都会有check属性啊,used是我随便取得一个变量名,用来标记
保持所有的checkbox以控制菜单整齐,以used属性标记是否显示check状态,实现了部分菜单的check属性
然后,讲完了这个,刚才漏的离开隐藏的实现呢?
记得label将焦点转移给checkbox吗?现在每个子菜单项都有checkbox了,只要submenu的每个item内部套一个label即可
终上所述一个CSS原生菜单样式出现了,
果然还是要再打一遍代码吗!!!要累死我了
<style> *:focus{outline:none} menu{ display:none; position:absolute; margin-top:0; top:0; margin-left:0; left:0; margin-right:0; right:0; height:21px; padding-left:1px; white-space:nowrap; background-color:#f0f0f0; user-select:none; cursor:default; } menu item{ display:inline-block; padding:3px 6px 2px 6px; font-size:12px; vertical-align:top; } menu submenu{ position:absolute; display:none; width:auto; margin-left:-6px; background-color:#fff; font-size:16px; box-shadow:1px 1px 16px #aaa; } menu item:hover{background-color:#ddd} menu:focus item:hover submenu{display:block} menu delims{display:inline;position:absolute;margin:-2px 2px} menu submenu item{display:block;width:auto} menu submenu label{display:block;margin:-4px 4px} menu submenu hr{display:inline-block;width:100%;margin:0} menu input{display:inline-block;width:0} menu span{display:inline-block;margin-left:-6px} menu span + span{margin-left:6px} menu submenu span + span{padding:6px} menu input + span{width:9px} menu input[used]:checked + span::before{content:"\2713"} </style> <menu tabindex="0" oncontextmenu="return false"> <item> <span></span> <span>文件(F)</span> <submenu> <item> <label> <input type="checkbox"> <span></span> <span>新建文件</span> </label> </item> <item> <label> <input type="checkbox"> <span></span> <span>新建项目</span> </label> </item> <hr> <item> <label> <input type="checkbox"> <span></span> <span>打开文件</span> </label> </item> <item> <label> <input type="checkbox"> <span></span> <span>打开文件夹</span> </label> </item> <item> <label> <input type="checkbox"> <span></span> <span>导入云端项目</span> </label> </item> </submenu> </item> <item> <span></span> <span>编辑(E)</span> <submenu> <item> <label> <input type="checkbox"> <span></span> <span>撤销</span> </label> </item> <item> <label> <input type="checkbox"> <span></span> <span>重做</span> </label> </item> <hr> <item> <label> <input type="checkbox"> <span></span> <span>剪切</span> </label> </item> </submenu> </item> </menu>
效果
看得见虽然并没有我最开始的那个菜单那么完美,没有变色,没有提示词,没有换语言,但是已经很好看了
Part 2: Shadow DOM
let shadow = menuParentElement.attachShadow({ mode: 'open' });
shadow.innerHTML = style;
shadow.appendChild(menu);
这就是我的代码引用shadow的代码,现在只有通过shadow或menuParentElement.shadowRoot才能访问内部,外界其他其他方法都与它无关了
如果mode的值是close就彻底不能修改内部的值了,
我们上面是生成一个菜单项(参考Part1)append进去;于是封装了;
但是ShadowDOM真的只是这样吗?我们的变色,换语言呢?
事实上现在我在ShadowDOM外部留了开关,就是通过:host()选择器;
ShadowDOM的内部CSS才能使用的选择器,可以判断外部父元素的值
比如 :host(.night) menu{}就确定了当外部Class 包括"night"时的 menu样式
我上面的换语言和变色都是写好的CSS通过添加删除class(打开关闭,开关)实现的。
<style> menu label:hover::after{color:#000} :host(.show) menu{display:block} :host(.night) menu{color:#eee;background-color:#2d2d30} :host(.night) menu item:hover{background-color:#555} :host(.night) menu delims{color:#aaa} :host(.night) menu submenu{background-color:#2d2d30;box-shadow:1px 1px 16px #fff} :host(.night) menu label::after{color:#fff} menu span + span + span{display:none} :host(.EN) menu span + span{display:none} :host(.EN) menu span + span + span{display:inline-block} :host(.EN) menu label:after{content:attr(EN-description)} :host(.english) menu span + span{display:none} :host(.english) menu span + span + span{display:inline-block} :host(.english) menu label:after{content:attr(EN-description)} </style>
至于Json接口
function createMenu(menuParentElement, menuItemJson, useShadowDOM) { let menu = 0; let style = 0; if (!arguments[3]) { menu = document.createElement("menu"); menu.setAttribute("tabindex", "0"); menu.setAttribute("oncontextmenu", "return false"); style = () => {/* <style> </style> */}; style = style.toString().split(/\n/).slice(1, -1).join('\n'); } else { menu = document.createElement("submenu"); } for (let i = 0; i < menuItemJson.length; i++) { if (menuItemJson[i]["label"] == "delims") { if (arguments[3]) { menu.appendChild(document.createElement("hr")); } else { menu.innerHTML += "<delims>|</delims>" } } else if (menuItemJson[i]["submenu"]) { if (!menuItemJson[i]["label"]) { menu.style.visibility = "hidden"; createMenu(menu, menuItemJson[i]["submenu"], false, true); menu.children[0].setAttribute("tabindex", "0"); menu.children[0].style.visibility = "visible"; menu.children[0].style.display = "block"; } else { let item = document.createElement("item"); item.style = menuItemJson[i]["style"]; item.innerHTML += `<span></span><span>${menuItemJson[i]["label"]}</span><span>${menuItemJson[i]["EN-label"]?menuItemJson[i]["EN-label"]:menuItemJson[i]["label"]}</span>`; createMenu(item, menuItemJson[i]["submenu"], false, true); menu.appendChild(item); } } else { menu.innerHTML += `<item style="${menuItemJson[i]["style"] ? menuItemJson[i]["style"] : ""}" onmouseup="${menuItemJson[i]["function"]}"><label description="${menuItemJson[i]["description"] ? menuItemJson[i]["description"] : ""}" EN-description="${menuItemJson[i]["EN-description"] ? menuItemJson[i]["EN-description"] : (menuItemJson[i]["description"] ? menuItemJson[i]["description"] : "")}"><input type="checkbox" ${menuItemJson[i]["checkable"] ? "used" : ""}><span></span><span>${menuItemJson[i]["label"] ? menuItemJson[i]["label"] : ""}</span><span>${menuItemJson[i]["EN-label"] ? menuItemJson[i]["EN-label"] : (menuItemJson[i]["label"] ? menuItemJson[i]["label"] : "")}</span></label></item>`; } } if (useShadowDOM) { let shadow = menuParentElement.attachShadow({ mode: 'open' }); shadow.innerHTML = style; shadow.appendChild(menu); return menu; } else { if (!arguments[3]) menuParentElement.innerHTML = style; menuParentElement.appendChild(menu); } }
比较普通,不过整个组件都不需要引入其他库,打算利用onmouseup 直接运行函数 / 广播事件 ,然后根据事件运行函数。
上面有三个可以注意的点
1.正则表达式避免拼接大段字符串
style = () => {/* <style> ####### </style> */};
style = style.toString().split(/\n/).slice(1, -1).join('\n');
2.字符串中${ }可插入表达式和变量
"############${menuItemJson[i]["style"] ? menuItemJson[i]["style"] : ""}###############"
3.函数变量arguments,函数的参数变量名是可有可无的(勿喷)
function a(){
return arguments[0]
}
b=1;
c=a(b)//1
Menu之外的话题
1.fetch真好用,如果你还在使用ajax,快放弃它(麻烦,或者依赖时代毒瘤Jq)
fetch一句话调用实例(注意传递的都是Promise)
fetch('menu.json').then((e) => e.json()).then((e) =>console.log(e));
//我使用Json传输,是为了延迟动态加载,提高主页面的加载速度
2.
我们使用ShadowDOM都是先封装进去,后来直接用
不过,其实ShadowDOM内容也并非只能有封装进去的值
就是利用Tamplate的Slot
我们封装进去一个带Slot的template,父元素里对应的slot就可以插入模板并显示出来,
涉及Custom Element,Shadow DOM
由于我没有用这个功能,我贴下MDN的代码
/*js*/
customElements.define('element-details',
class extends HTMLElement {
constructor() {
super();
const template = document
.getElementById('element-details-template')
.content;
const shadowRoot = this.attachShadow({mode: 'open'})
.appendChild(template.cloneNode(true));
}
});
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>element-details - web component using <template> and <slot></title> <style> dl { margin-left: 6px; } dt { font-weight: bold; color: #217ac0; font-size: 110% } dt { font-family: Consolas, "Liberation Mono", Courier } dd { margin-left: 16px } </style> </head> <body> <h1>element-details - web component using <code><template></code> and <code><slot></code></h1> <template id="element-details-template"> <style> details {font-family: "Open Sans Light",Helvetica,Arial} .name {font-weight: bold; color: #217ac0; font-size: 120%} h4 { margin: 10px 0 -8px 0; } h4 span { background: #217ac0; padding: 2px 6px 2px 6px } h4 span { border: 1px solid #cee9f9; border-radius: 4px } h4 span { color: white } .attributes { margin-left: 22px; font-size: 90% } .attributes p { margin-left: 16px; font-style: italic } </style> <details> <summary> <span> <code class="name"><<slot name="element-name">NEED NAME</slot>></code> <i class="desc"><slot name="description">NEED DESCRIPTION</slot></i> </span> </summary> <div class="attributes"> <h4><span>Attributes</span></h4> <slot name="attributes"><p>None</p></slot> </div> </details> <hr> </template> <element-details> <span slot="element-name">slot</span> <span slot="description">A placeholder inside a web component that users can fill with their own markup, with the effect of composing different DOM trees together.</span> <dl slot="attributes"> <dt>name</dt> <dd>The name of the slot.</dd> </dl> </element-details> <element-details> <span slot="element-name">template</span> <span slot="description">A mechanism for holding client- side content that is not to be rendered when a page is loaded but may subsequently be instantiated during runtime using JavaScript.</span> </element-details> <script src="main.js"></script> </body> </html>
效果
好的,你应该可以打出自己的Menu组件了,当然我也不介意你使用我的代码
补充:使用customElement来写每个菜单子项也不错
关于上面我所有的代码如果有问题请联系我
或者你有更好的方法也请不吝赐教。
愿意交流我也随时恭候。
期待我的下篇博客就收藏吧