I am using NgbTypeahead component of ng-bootstrap. My problem is, when I put the typeahead component inside a scrollable component and make a scroll down, the position of dropdo
To add a vertical scroll bar to typeahead results, you can use something this:
ngb-typeahead-window.dropdown-menu {
max-height: 500px !important;
overflow-y: auto;
}
As NgbTypeahead does not have support with scroll, we need to handle from component. Use showDropdownEle function on keydown of Input.
private isElementInViewport(el, inputElem) {
const rect = el.getBoundingClientRect();
const rectElem = inputElem.getBoundingClientRect();
console.log(rectElem);
return (
rect.top >= rectElem.bottom &&
rect.left >= 0 &&
rect.bottom <= (rectElem.bottom + rect.offsetHeight) &&
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
);
}
public showDropdownEle(event) {
if (event.keyCode === 38 || event.keyCode === 40) {
if (event.target.nextElementSibling && event.target.nextElementSibling.nodeName === 'NGB-TYPEAHEAD-WINDOW') {
let activeDropdownEle = (event.keyCode === 40) ? event.target.nextElementSibling.querySelector('.active').nextElementSibling : event.target.nextElementSibling.querySelector('.active').previousElementSibling;
if (!activeDropdownEle) {
const allDropdownElems = event.target.nextElementSibling.querySelectorAll('.dropdown-item');
activeDropdownEle = (event.keyCode === 38) ? allDropdownElems[allDropdownElems.length - 1] : allDropdownElems[0];
}
if (!this.isElementInViewport(activeDropdownEle, event.target) && event.keyCode === 40) {
activeDropdownEle.scrollIntoView(false);
}
if (!this.isElementInViewport(activeDropdownEle, event.target) && event.keyCode === 38) {
activeDropdownEle.scrollIntoView(true);
}
}
}
}
typeahead-scrollable.html file
<input id="typeahead-scrollable" type="text" class="form-control" (keydown)="typeaheadKeydown($event)" #typeaheadInstance="ngbTypeahead" [(ngModel)]="model" [ngbTypeahead]="search" [resultFormatter]="formatter"
typeahead-scrollable.ts file
@ViewChild('typeaheadInstance')
private typeaheadInstance: NgbTypeahead;
typeaheadKeydown($event: KeyboardEvent) {
if (this.typeaheadInstance.isPopupOpen()) {
setTimeout(() => {
const popup = document.getElementById(this.typeaheadInstance.popupId);
const activeElements = popup.getElementsByClassName('active');
if (activeElements.length === 1) {
// activeElements[0].scrollIntoView();
const elem = (activeElements[0] as any);
if (typeof elem.scrollIntoViewIfNeeded === 'function') {
// non standard function, but works (in chrome)...
elem.scrollIntoViewIfNeeded();
} else {
//do custom scroll calculation or use jQuery Plugin or ...
this.scrollIntoViewIfNeededPolyfill(elem as HTMLElement);
}
}
});
}
}
private scrollIntoViewIfNeededPolyfill(elem: HTMLElement, centerIfNeeded = true) {
var parent = elem.parentElement,
parentComputedStyle = window.getComputedStyle(parent, null),
parentBorderTopWidth = parseInt(parentComputedStyle.getPropertyValue('border-top-width')),
parentBorderLeftWidth = parseInt(parentComputedStyle.getPropertyValue('border-left-width')),
overTop = elem.offsetTop - parent.offsetTop < parent.scrollTop,
overBottom = (elem.offsetTop - parent.offsetTop + elem.clientHeight - parentBorderTopWidth) > (parent.scrollTop + parent.clientHeight),
overLeft = elem.offsetLeft - parent.offsetLeft < parent.scrollLeft,
overRight = (elem.offsetLeft - parent.offsetLeft + elem.clientWidth - parentBorderLeftWidth) > (parent.scrollLeft + parent.clientWidth),
alignWithTop = overTop && !overBottom;
if ((overTop || overBottom) && centerIfNeeded) {
parent.scrollTop = elem.offsetTop - parent.offsetTop - parent.clientHeight / 2 - parentBorderTopWidth + elem.clientHeight / 2;
}
if ((overLeft || overRight) && centerIfNeeded) {
parent.scrollLeft = elem.offsetLeft - parent.offsetLeft - parent.clientWidth / 2 - parentBorderLeftWidth + elem.clientWidth / 2;
}
if ((overTop || overBottom || overLeft || overRight) && !centerIfNeeded) {
elem.scrollIntoView(alignWithTop);
}
}
Working example:
https://stackblitz.com/edit/angular-utd9ii?file=app%2Ftypeahead-scrollable.ts
Place the following code in your styles.css.
We can place the following code in any of the following files
ngb-typeahead-window {
max-height: 200px;
overflow-y: auto;
overflow-x: hidden;
}
Working code stackblitz link: https://stackblitz.com/edit/angular-qpzsfv