VueJS Header responsive & Loader

Vue JS Header responsive dropdown menu.

Vue-JS-Header-responsive-dropdown-menu

Made with

Html
Css/LESS
Javascript

Html

  	<div id="app">
		<link href="https://fonts.googleapis.com/css?family=Roboto|Poppins" rel="stylesheet">
		<meta name="viewport" content="width=device-width">
		<header>
			<div class="wrap">
				<div id="hamburger" v-on:click="display_menu()">
					<span></span>
					<span></span>
					<span></span>
				</div>
				<img src="https://humancoders-formations.s3.amazonaws.com/uploads/course/logo/104/thumb_bigger_formation-vue-js.png" alt="logo">
				<nav id="menu">
					<li><a>Menu 1</a></li>
					<li class="drop"><a v-on:click="display_drop_menu()">Menu 2 dropdown <i class="icon-arrow"></i></a>
						<ul class="drop_menu">
							<a>Sub menu 1</a>
							<a>Sub menu 2</a>
							<a>Sub menu 3</a>
						</ul>
					</li>
					<li class="drop"><a v-on:click="display_drop_menu()">Menu 3 dropdown  <i class="icon-arrow"></i></a>
						<ul class="drop_menu">
							<a>Sub menu 1</a>
							<a>Sub menu 2</a>
							<a>Sub menu 3</a>
						</ul>
					</li>
					<li><a>Menu 4</a></li>
				</nav>
			</div>
		</header>
		<div id="loader" v-if="load">
			<span></span>
			<span></span>
			<span></span>
			<span></span>
			<span></span>
			<span></span>
			<span></span>
			<span></span>
			<span></span>
		</div>
		<div id="overlay" v-if="load"></div>
		<div id="background">
			<h3>This menu is responsive, resized on.</h3>
			<h3>The header is hide when scroll down and show when scroll up, try it !</h3>
			<button v-on:click="loaded()">Click here for loader!</button></button>
		</div>
	</div>

Css

@cblue : #41B883;
@cgrey : #3E3A37;
@cwhite : #ffffff;
@ccream : #FAFBFC;
@cgrey_green: #435466;
@clightgrey : #DAD9D7;

@body_height_8: calc((100vh - 113px)/8);
@body_height_4: calc((100vh - 113px)/4);
.icon-arrow {
	mask:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 284.929 284.929'><path d='M282.082,76.511l-14.274-14.273c-1.902-1.906-4.093-2.856-6.57-2.856c-2.471,0-4.661,0.95-6.563,2.856L142.466,174.441		L30.262,62.241c-1.903-1.906-4.093-2.856-6.567-2.856c-2.475,0-4.665,0.95-6.567,2.856L2.856,76.515C0.95,78.417,0,80.607,0,83.082c0,2.473,0.953,4.663,2.856,6.565l133.043,133.046c1.902,1.903,4.093,2.854,6.567,2.854s4.661-0.951,6.562-2.854L282.082,89.647c1.902-1.903,2.847-4.093,2.847-6.565C284.929,80.607,283.984,78.417,282.082,76.511z'/></svg>");
}
i {
	vertical-align: middle;
	display: inline-block;
	background-repeat: no-repeat;
}

html, body {margin: 0;width: 100%;height: 100%; font-size: 14px; font-family: 'Roboto', sans-serif;}
* {box-sizing: border-box;}
body {background-color: @ccream;padding-top: 113px; overflow-x: hidden; &.display_menu{overflow-y: hidden;}}
ul {padding: 0; margin: 0;}
li {list-style: none;}
.wrap {max-width: 1024px; min-width: 320px;}
header {
	display: flex;
	z-index: 1;
	padding: 10px 0;
	background-color: @cwhite;
	position: fixed;
	width: 100%;
	top: 0;
	justify-content: center;
	box-shadow: 0 0 10px 0 rgba(0, 0, 0, .1);
	transition: 0.33s;
}
header .wrap {
	padding: 0 2%;
	width: 100%;
	display: flex;
	align-items: flex-end;
	justify-content: space-between;
}
header img {
	width: 100px;
}
header #menu {
	display: flex;
	flex-direction: row;
}
header #menu li {
	position: relative;
	user-select: none;
	margin-left: 20px;
	i {width: 12px; height: 12px; background-color: @cgrey;}
	a {
		color: @cgrey;
		cursor: pointer;
		font-size: 1.15em;
		border: none;
		border-bottom: 2px solid @cwhite;
		transition: 0.15s;
		background: none;
		&:hover {
			border-bottom-color: @cblue;
			color: @cblue;
			i {
			background-color: @cblue;
			}
		}
	}
}
header .drop_menu {
	position: absolute;
	display: block;
	top: 149%;
	transform: scaleY(0);
	width: auto;
    transform-origin: top;
    background-color: @cwhite;
	transition: 0.25s;
	padding-bottom: 10px;
	box-shadow: 0 2px 5px 0 rgba(0, 0, 0, .1);
	a {
		margin-left: 20px;
		display: block;
		transition: 0.45s;
		opacity: 0;
		margin-right: 20px;
		padding: 10px 0 0 0;
	}
}

header .drop_menu.display {
	transform: scaleY(1);
	a {
		opacity: 1;
	}
}
header #hamburger {
	cursor: pointer;
	border-radius: 50%;
	position: absolute;
	left: 3%;
	top: 30px;
	display: none;
	transform: translateY(-50%);
}
header #hamburger span {
	height: 2px;
	margin-top: 5px;
	margin-bottom: 5px;
	background-color: @cgrey;
	display: block;
	transition: 0.33s;
	&:nth-child(1) {width: 12px;}
	&:nth-child(2) {width: 24px;}
	&:nth-child(3) {width: 12px;}
}
.display_menu header #hamburger {
	span:nth-child(1) {transform: rotate(45deg) translate(2px, 1px); }
	span:nth-child(2) {transform: rotate(-45deg) ;}
	span:nth-child(3) {transform: rotate(45deg) translate(6px, -9px);}
}
#background {
	content: '';
	display: block;
	width: 100vw;
	height: 200vh;
	padding: 5%;
	background-color: @cblue;
	h3 {
		margin: 0;
		margin-bottom: 1%;
		font-size: 1.5em;
		text-align: center;
		color: @cwhite;
		i {
			width: 13px;
			height: 20px;
			background-color: @cwhite;
		}
	}
	button {
		position: relative;
		left: 50%;
		transform: translateX(-50%);
		cursor: pointer;
		padding: 5px 10px;
		font-size: 1.3em;
		border:none;
		background-color: @cgrey_green;
		color: @cwhite;
	}
}
#loader {
	display: block;
	width: 50px;
	height: 50px;
	position: fixed;
	top: 50%;
	left: 50%;
	z-index: 3;
	margin-left: -25px;
	margin-top: -25px;
	animation: spin 1s infinite linear;
	span {
	    display: block;
	    width: 100%;
	    height: 100%;
	    border-radius: 50%;
	    position: absolute;
	    top: 0;
	    left: 0;
	    border-width: 0px 0px 10px 10px;
	    border-style: solid;
	    border-color: transparent;
		&:nth-child(0) {border-left-color: @cblue;}
	    &:nth-child(1) {transform: rotate(-40deg);  border-left-color: @cblue;}
	  	&:nth-child(2) {transform: rotate(-80deg);  border-left-color: @cblue;}
	  	&:nth-child(3) {transform: rotate(-130deg);	border-left-color: @cgrey_green;}
	  	&:nth-child(4) {transform: rotate(-170deg);	border-left-color: @cgrey_green;}
	  	&:nth-child(5) {transform: rotate(-210deg);	border-left-color: @cgrey_green;}
	  	&:nth-child(6) {transform: rotate(-250deg);	border-left-color: @cgrey;}
	  	&:nth-child(7) {transform: rotate(-280deg);	border-left-color: @cgrey;}
	  	&:nth-child(8) {transform: rotate(-320deg);	border-left-color: @cgrey;}
	  	&:nth-child(9) {transform: rotate(-360deg);	border-left-color: @cblue;}
	}
}
@keyframes spin { from {transform: rotate(0deg);} to {transform: rotate(-360deg);}}
#overlay {
	display: block;
	position: fixed;
	top: 0;
	bottom: 0;
	left: 0;
	right: 0;
	z-index: 2;
	background-color: @cgrey;
	opacity: 0.5;
}
@media screen and (max-width: 660px) {
	body {padding-top: 70px;}
	header .wrap {
		justify-content: center;
		align-items: center;
		flex-direction: column;
		padding: 0;
	}
	header img {width: 50px;}
	header #hamburger {
		display: block;
	}
	header #menu {
		width: 100%;
		display: block;
     	height: 0;
    	transform-origin: 50% 0;
     	transition: 0.33s ease;
		flex-direction: column;
	}
	.display_menu header #menu {
     	height: calc(100vh - 64px);
		li {
     		height: @body_height_8;
			border-bottom: 1px solid @clightgrey;
     		transition: 0.25s ease;
			opacity: 1;
			display: block;
			a {
			}
		}
	}
	header #menu li {
		height: 0;
		opacity: 0;
		margin-left: 0;
     	transition: 0.25s ease;
		a {
			left: 0;
     		line-height: @body_height_8;
			padding-left: 20px;
			border: none;
			height: 100%;
			width: 100%;
			display: block;
			&:hover {
				color: @cwhite;
				background-color: @cblue;
				i {background-color: @cwhite;}
			}
		}
		i {
			position: absolute;
			right: 20px;
			top: 50%;
			transform: translateY(-50%);
			background-color: @cgrey;
		}
	}
	header .drop_menu {
		top: @body_height_8;
		box-shadow: none;
		left: 0;
		padding-bottom: 0;
		width: 100%;
		a {
			width: 100%;
			padding: 0 !important;
			padding-left: 40px !important;
			margin: 0;
			border-bottom: 1px solid @clightgrey !important;
		}
	}
}
@media screen
and(max-width: 660px)
and(max-height: 500px){
	
	.display_menu header  {
		max-height: 100vh;
		overflow-y: scroll;
	}
	.display_menu header #menu {
		li {
     		height: @body_height_4;
     	}
	}
	header #menu li {
		a {
     		line-height: @body_height_4;
     	}
    }
    header .drop_menu {
		top: @body_height_4;
	}
}
i {
	vertical-align: middle;
	display: inline-block;
	background-repeat: no-repeat;
}

Javascript


window.addEventListener("resize", function(event) {
	close_all_menu();
	document.getElementsByTagName("body")[0].classList.remove("display_menu");
});
var last_scroll = 0;
window.onscroll = function() {
	if(!document.getElementById("loader")){
		close_all_menu();
		var header = document.getElementsByTagName("header")[0];
		if(Math.abs(last_scroll - this.scrollY) <= 5) return;
		(this.scrollY < last_scroll) ? header.style.top = "0" : header.style.top = "-" + header.clientHeight + "px" ;
		last_scroll = this.scrollY;
	}
}
var app = new Vue({
  el: '#app',
  data: {
			load : false,
	},
	methods: {
		display_menu : function(){
			var body = document.getElementsByTagName("body")[0];
			(!body.classList.contains("display_menu")) ? body.classList.add("display_menu") : body.classList.remove("display_menu");
		},
		display_drop_menu : function(){
			var drop_menu = event.target.parentElement.getElementsByClassName("drop_menu")[0];
			var drop_menus = document.getElementsByClassName("drop_menu");

			Array.from(drop_menus).forEach(function(e){
				if(e != drop_menu){
					e.classList.remove("display");
				}
			});
			var lis = document.getElementById("menu").getElementsByTagName("li");
			Array.from(lis).forEach(function(e){
				e.style.marginTop = 0;
			});
			(!drop_menu.classList.contains("display")) ? drop_menu.classList.add("display") : drop_menu.classList.remove("display");
			if(window.innerWidth < 660 && drop_menu.classList.contains("display")) {
				event.target.parentElement.nextSibling.nextSibling.style.marginTop = drop_menu.clientHeight + "px";
			}
		},
		loaded : function(){
        	document.getElementsByTagName("body")[0].style.overflowY = "hidden";
        	this.load = true;
		}
	}
});
function close_all_menu() {
	var lis = document.getElementById("menu").getElementsByTagName("li");
	Array.from(lis).forEach(function(e){
		e.style.marginTop = 0;
	});
	var drop_menus = document.getElementsByClassName("drop_menu");
	Array.from(drop_menus).forEach(function(e){
		e.classList.remove("display");
	});
}

Author

Raphaël

Demo

See the Pen VueJS Header responsive & Loader by Raphaël (@raphaelbensimon) on CodePen.