[HTMLパーツ] メニュー項目サンプル #1「ヘッダメニュー」

2019年2月11日

HTML テクノロジー プログラミング

HTMLの部品を組み合わせて作るホームページはショボい、という声を周囲のエンジニアからよく聞きますが、ショボいかどうかは最終的に仕上げるクリエーターの腕次第だと言うことが考えられます。 もはやコピペでしかコーディングしていないプログラマーも、デザインが苦手なプログラマーも、一生懸命自分流を貫いているエンジニアも、ショボくなく素敵なWEBシステム(サービスや、ホームページ)を開発することを目的としているのは間違いありません。 とりあえず、僕個人的には「自分用WEBページパーツ」というのをストックして、デザインを少しずつ変えながら利用しているのですが、そういうストックをたくさん持っているほど作業効率は増します。 確かにコピペ作業といえばそうかもしれませんが、手打ちする作業を簡略化できる要素として行っているのでHTMLパーツの自分ライブラリは効率を求めるエンジニアにオススメできます。 というわけで、オレライブラリの紹介を小出しにしていく予定ですが、今回はホームーページには不可欠なヘッダメニューですが、サブメニューが表示するモノを構築しようとすると意外とめんどくさいので、簡素な作りの構造を作っておくことで、あとはclassやidを追加してシステム対応すればいい状態になります。 そんなメニューのサンプルのソースコードを紹介します。

メニューサンプル-DEMO

See the Pen Menu-1 by YugetaKoji (@geta1972) on CodePen.

機能説明

ホームページの上部に固定するタイプのヘッダメニューで、URLでリンクのものと、プルダウンメニューの混在するものです。 機能が階層化されているようなWEBシステムなどのヘッダで利用すると便利に使えますね。 プルダウンするメニューには、▼マークをつけて階層が有ることを表し、改装メニューの表示。非表示は、若干のcssアニメーションをつけて存在感を表しています。 クリック判定でdata-view属性を切り替えることで設置されているcssでの表示処理やアニメーション処理を行っています。 また、サブメニューを表示している状態で、画面のそれ以外の場所をクリックした場合に、サブメニューを閉じるという処理を追加しています。

ソースコード

<html lang="en"> <head> <meta charset="utf-8"> <title>HTML-parts Header</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="menu.css"> <script src="menu.js"></script> </head> <body> <div class="menu-bar"> <ul> <li> <a href="javascript:void(0)">Menu-1</a> <ul> <li><a href="#menu-1-1">Menu-1-1</a></li> <li><a href="#menu-1-2">Menu-1-2</a></li> </ul> </li> <li> <a href="#menu-2">Menu-2</a> </li> </ul> <ul> <li> <a href="javascript:void(0)">Menu-3</a> <ul> <li><a href="#menu-3-1">Menu-3-1</a></li> <li><a href="javascript:void(0)">Menu-3-2</a></li> </ul> </li> </ul> </div> </body> </html> html,body{ margin : 0; padding : 0; border : 0; width : 100%; height : 100%; } *, *:before, *:after { -webkit-box-sizing : border-box; -moz-box-sizing : border-box; -o-box-sizing : border-box; -ms-box-sizing : border-box; box-sizing : border-box; } .menu-bar{ height : 60px; border-bottom : 1px solid #AAA; line-height : 60px; display : -webkit-flex; display : flex; -webkit-justify-content : center; justify-content : center; -webkit-align-items : center; align-items : center; } /* basic */ .menu-bar ul{ margin : 0; padding : 0; border : 0; } .menu-bar ul li{ margin : 0; padding : 0; border : 0; list-style : none; } .menu-bar ul li a{ display : inline-block; height : 100%; padding : 0 8px; cursor : pointer; text-decoration : none; } /* .menu-bar ul li a:hover{ opacity : 0.5; } */ /* top-menu */ .menu-bar > ul{ display : flex; height : 100%; min-width : 100px; } .menu-bar > ul > li{ position : relative; } .menu-bar > ul > li > a{ min-width : 100px; text-align:center; } .menu-bar > ul > li > a:hover{ background-color:#eee; } .menu-bar > ul > li > a[data-active="1"]{ background-color:#ccc; } .menu-bar > ul > li > a[data-active="0"]{ animation: menu-noactive 0.3s linear forwards; } .menu-bar > ul > li.dropdown > a:after, .menu-bar > ul > li[data-dropdown="1"] > a:after{ content: ""; display: inline-block; width: 0; height: 0; vertical-align: middle; border-top: 6px dashed; border-right: 4px solid transparent; border-left: 4px solid transparent; box-sizing: border-box; margin-left:4px; } /* second-menu */ .menu-bar > ul > li > ul{ position : absolute; left : 0; line-height : 30px; border:1px solid #aaa; border-width:1px 1px 0; box-shadow : 2px 2px 8px rgba(0,0,0,0.4); opacity : 0.0; visibility : hidden; } .menu-bar > ul > li > ul[data-view="1"]{ visibility : visible; opacity : 1.0; } .menu-bar > ul > li > ul[data-view="0"]{ animation: menu-close 0.3s linear forwards; } .menu-bar > ul > li > ul[data-view="0"] > li{ animation: menu-list-close 0.1s linear forwards; } .menu-bar > ul > li > ul[data-view="1"] > li{ animation: menu-list-open 0.1s linear forwards; } .menu-bar > ul > li > ul > li{ border-bottom : 1px solid #aaa; overflow:hidden; } .menu-bar > ul > li > ul > li > a{ min-width : 120px; text-align:center; } .menu-bar > ul > li > ul > li > a:hover{ background-color : #eee; } @keyframes menu-noactive{ 0%{ background-color:#ccc; } 100%{ background-color:rgba(255,255,255,0.0); } } @keyframes menu-close{ 0%{ opacity:1.0; visibility:visible; } 100%{ opacity:0.0; visibility:hidden; } } @keyframes menu-list-open{ 0%{ height:0px; } 100%{ height:30px; } } @keyframes menu-list-close{ 0%{ height:30px; } 100%{ height:0px; } } ;(function(){ // ページ内にjsライブラリの読み込み var $$addScript = function(file){ var s = document.createElement("script"); s.src = file; document.body.appendChild(s); } // イベントライブラリ var $$event = function(target, mode, func){ //other Browser if (typeof target.addEventListener !== "undefined"){ target.addEventListener(mode, func, false); } else if(typeof target.attachEvent !== "undefined"){ target.attachEvent('on' + mode, function(){func.call(target , window.event)}); } }; var $$ = function(){ var state = document.readyState; if(state === "complete"){ this.start(); } else if(state === "interactive"){ $$event(window , "DOMContentLoaded" , (function(e){this.start(e)}).bind(this)); } else{ $$event(window , "load" , (function(e){this.start(e)}).bind(this)); } }; $$.prototype.start = function(){ // top-menu var top_menus = document.querySelectorAll(".menu-bar > ul > li > a"); for(var i=0; i<top_menus.length; i++){ $$event(top_menus[i] , "click" , (function(e){this.set_top_menu_toggle(e)}).bind(this)); } // dropdown-set this.set_dropdown(); // other-click $$event(window , "click" , (function(e){this.checkClick(e)}).bind(this)); }; $$.prototype.set_top_menu_toggle = function(e){ var target = e.currentTarget; var top_menus = document.querySelectorAll(".menu-bar > ul > li > a"); for(var i=0; i<top_menus.length; i++){ var dropdown = top_menus[i].parentNode.querySelector(":scope > ul"); // data-view if(dropdown && top_menus[i] === target && dropdown.getAttribute("data-view") !== "1"){ dropdown.setAttribute("data-view" , "1"); top_menus[i].setAttribute("data-active" , "1"); } else{ if(dropdown && dropdown.getAttribute("data-view") === "1"){ dropdown.setAttribute("data-view" , "0"); } if(top_menus[i].getAttribute("data-active") === "1"){ top_menus[i].setAttribute("data-active" , "0"); } } } }; $$.prototype.checkClick = function(e){ var target = e.target; // console.log(target); if(!target || !target.matches(".menu-bar > ul > li > a")){ this.set_top_menu_toggle({currentTarget:null}); } }; $$.prototype.set_dropdown = function(){ var top_menus = document.querySelectorAll(".menu-bar > ul > li > ul"); for(var i=0; i<top_menus.length; i++){ top_menus[i].parentNode.setAttribute("data-dropdown" , "1"); } }; new $$; })();

解説

HTMLはclass="menu-bar"というヘッダの親要素に対して、それ以下のタグはシンプルな状態で設置できるようにしています。 見てもらうとわかりますが、LIの中にULが存在していると階層ありという処理をして、JSがページ読み込み時に属性フラグを設置してます。 CSSでの注意点は、コピペで利用する時に、ソース上部にある2つの基本コードは省いたほうがコンフリクトしなくなります。 ※今回はデモ用でつけているだけです。

改良ポイント

このツールのデメリットはJS処理をしているのですが、CSSのみでこうした処理ができる仕組みであれば、サイトのスペック向上をすることが可能になります。 CSSだけ版というものを次回以降にサンプルで紹介したいと思います。

このブログを検索

ごあいさつ

このWebサイトは、独自思考で我が道を行くユゲタの少し尖った思考のTechブログです。 毎日興味がどんどん切り替わるので、テーマはマルチになっています。 もしかしたらアイデアに困っている人の助けになるかもしれません。