Reputation: 359
I have built an Angular 4 web app. And I want my navbar to be automatically hidden when I move the mouse.There is an example i want to use https://codepen.io/JFarrow/pen/fFrpg. I have read some document online, but I still can not figure out how.. Here is my navmenu.component.html code:
<meta http-equiv="pragma" content="no-cache" />
<link rel="stylesheet" type="text/css" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
<script type="text/javascript" src="jquery-1.8.3.js"></script>
<div class='main-nav'>
<div class='navbar navbar-inverse'>
<div class='navbar-header'>
<a class='navbar-brand' [routerLink]="['/home']">Navbar</a>
<button type='button' class='navbar-toggle' data-toggle='collapse' data-target='.navbar-collapse'>
<span (click)="isCollapsed = !isCollapsed">Toggle navigation</span>
<span class='icon-bar'></span>
<span class='icon-bar'></span>
<span class='icon-bar'></span>
<span class='icon-bar'></span>
</button>
</div>
<div class='clearfix'></div>
<div class='navbar-collapse collapse' >
<ul class='nav navbar-nav'>
<li [routerLinkActive]="['link-active']">
<a [routerLink]="['/home']">
<span class='glyphicon glyphicon-home'></span> Home
</a>
</li>
<li [routerLinkActive]="['link-active']">
<a [routerLink]="['/login']">
<span class='glyphicon glyphicon-log-in'></span> Log In
</a>
</li>
<li [routerLinkActive]="['link-active']">
<a [routerLink]="['/margin']">
<span class='glyphicon glyphicon-list'></span> Report
</a>
</li>
<li [routerLinkActive]="['link-active']">
<a [routerLink]="['/smart-table']">
<span class='glyphicon glyphicon-list'></span> Smart table
</a>
</li>
</ul>
</div>
</div>
</div>
And my navmenu.component.ts:
import { Component } from '@angular/core';
@Component({
selector: 'nav-menu',
templateUrl: './navmenu.component.html',
styleUrls: ['./navmenu.component.css']
})
export class NavMenuComponent {
public isCollapsed: boolean = false;
public collapsed(event: any): void {
console.log(event);
}
public expanded(event: any): void {
console.log(event);
}
}
And my navmenu.component.css:
li .glyphicon {
margin-right: 10px;
}
/* Highlighting rules for nav menu items */
li.link-active a,
li.link-active a:hover,
li.link-active a:focus {
background-color: #4189C7;
color: white;
}
/* Keep the nav menu independent of scrolling and on top of other items */
.main-nav {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 1;
}
@media (min-width: 768px) {
/* On small screens, convert the nav menu to a vertical sidebar */
.main-nav {
height: 100%;
width: calc(25% - 20px);
}
.navbar {
border-radius: 0px;
border-width: 0px;
height: 100%;
}
.navbar-header {
float: none;
}
.navbar-collapse {
border-top: 1px solid #444;
padding: 0px;
}
.navbar ul {
float: none;
}
.navbar li {
float: none;
font-size: 15px;
margin: 6px;
}
.navbar li a {
padding: 10px 16px;
border-radius: 4px;
}
.navbar a {
/* If a menu item's text is too long, truncate it */
width: 100%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
And I am not sure where should I add the code. There are codes online about adding a js function, or using some buid in bootstrap function, just not work for me....
Appreciate your help.
Upvotes: 4
Views: 427
Reputation: 3062
There are several ways you could do it, but here's how I would do it. You will need to add a scroll event listener either on the entire page, or the main body / content part of the page. If your content portion of the page was its own component, and the navbar was its own separate component, you may have a template with something like this.
<navbar-component></navbar-component>
<content-component></content-component>
In that case, you could add a scroll listener to the content component that toggles some variable while scrolling is happening, and then returns it when scrolling stops (assuming that is the behavior you want). To do that you might need to use a debounce and timeout during the scroll. something like:
<content-component (scroll)="scrollFunction()"></content-component>
.... and then somewhere in the code for the component that hosts both the navbar and content ...
timeout: any;
isScrolling: boolean = false;
scrollFunction() : void {
this.isScrolling = true;
clearTimeout(this.timeout);
this.timeout = setTimeout(()=> {
this.isScrolling = false
}, 1000);
}
That 1000 number is num of milliseconds, and you could set it to whatever you want, but basically, that function would just set the isScrolling variable to true while the user is scrolling, and once they stop scrolling it would eventually be reset to false after that time. The clearTimeout thing makes sure the timer keeps being reset back to 0 as long as scrolling continues to occur. If you used this method, then you would want to add a class binding to the navbar component that added some class while isScrolling was true, and removed it when it wasn't.
<navbar-component [class.navbar-hidden]="isScrolling"></navbar-component>
<content-component (scroll)="scrollFunction()"></content-component>
I suggest adding the scroll listener to the specific content element rather than the document or window object because that makes your app more decoupled from the DOM. But the typical jQuery way of doing things would be to just add that scroll listener to the entire document or window object and toggle the class with that. If your app was going to stay small and simple and you knew it was going to always be used inside a browser, there'd really be no problem with doing it that way. You can add window event listeners anywhere by just changing
(scroll)="scrollFunction()"
to
(window:scroll)="scrollFunction()"
If your content-component is not a separate component, and it contains the navbar-component within it (i.e, if you were just using the main app component for the body, and had the navbar component inside that), you could use HostListener instead of the template event binding to listen for the event. So instead of adding that '(scroll)' thing in the HTML, you would import host listener:
import { Component, HostListener, ....(anything else you have...) } from '@angular/core'
and then in the component:
@HostListener('scroll') myScrollFunction() {
... same code here as above....
}
That will accomplish the same thing, triggering that function any time the scroll event is detected inside the component. You would probably want to add a CSS transition to the navbar-hidden class, so the navbar would slide or fade neatly in and out of view. :-)
EDIT: I've noticed that on many sites, instead of simply having the navbar reappear after a certain time of not scrolling, they will have it appear only when the user starts to scroll back upward and it will always be hidden on a downward scroll. If you did it like that you could avoid using a timeout and just examine the scroll event to see if its scrolling up or down, and toggle the isScrolling variable based on that. To pass the event data as a parameter, in your template:
<navbar-component [class.navbar-hidden]="isScrollingDown"></navbar-component>
<content-component (scroll)="scrollFunction($event)"></content-component>
and then in your component...
isScrollingDown: boolean;
lastScrollPosition: number;
scrollFunction($event) : void {
let newPosition = $event.srcElement.scrollTop;
if (newPosition <= this.lastScrollPosition){
isScrollingDown = false;
}
else {
isScrollingDown = true;
}
this.lastScrollPosition = newPosition; // store the last position so you can compare the new and old position with each function call
}
scrollTop is just the name of the DOM attribute that specifies scroll position for each element.
If you go the HostListener route, here is how you would pass the $event parameter to that.
@HostListener('scroll', ['$event']) myScrollFunction($event){
... similar code here...
}
Upvotes: 1