How to scroll mat-select option top after closing the mat-select? - javascript

How to scroll mat-select option top after closing the mat-select?
I have many option in my mat-select. I select one option from bottom. Now, after open mat-select again, I want the list from top.

You have to subscribe to openChange event and reset scroll position of the scrollable element every time MatSelect opens:
#ViewChild('selectElement') selectElement: MatSelect;
ngOnInit() {
this.selectElement.openedChange.subscribe((isOpen: boolean) => {
if (isOpen) {
const panel = this.selectElement.panel.nativeElement as HTMLDivElement;
panel.scrollTop = 0;
}
});
}
Example on StackBlitz.

Related

Is there a way to move to a specific index when an item is added in the Angular?

i have some list and can add/remove item of the list.
Is there a way to cause an event to scroll to a specific index (where the item was added) when the item is added? In the current example, the item is added to the front of the list, so the scroll must be moved to the top
for example, when i'm in the middle(or bottom) of the list, if i add item to the list, the scroll move to the top of the list. (or move to some index, this case, index 0).
Tell me how to scroll from parent to child components without changing the structure of the example.
example: https://stackblitz.com/edit/angular-ivy-ymbsj7
Your problem is that ViewChild wont go into deeper levels when querying, so you cant query for a CdkVirtualScrollViewport in a child elements template. I could solve this with a custom change detection function in your list component.
You should remove this from your your app.ts -> addItem() function:
// want to move scroll to the top of the list
this.viewPort.scrollToIndex(0, 'smooth');
and instead create a custom change detection function in your list component, but first move the viewChild of the CdkVirtualScrollViewport to the list component:
export class ListComponent {
#ViewChild(CdkVirtualScrollViewport) viewPort: CdkVirtualScrollViewport;
#Input()
data: Favorite[];
#Output()
removeData = new EventEmitter<Favorite>();
remove($event) {
this.removeData.emit($event);
}
ngOnChanges(changes: SimpleChanges) {
if (
changes.data &&
changes.data.currentValue &&
changes.data.previousValue &&
changes.data.currentValue.length >changes.data.previousValue.length
) {
this.viewPort.scrollToIndex(0, 'smooth');
}
}
}
this works perfectly for me. Every time an item is added, it scrolls to the top.
Modified stackblitz link:
https://stackblitz.com/edit/angular-ivy-k5pve6?file=src/app/list/list.component.ts
Another solution(and maybe better) could be passing the ListComponent as a template reference to the addItem() function, then use the compononents viewPort property's scroll function.
List Component
...
export class ListComponent {
#ViewChild(CdkVirtualScrollViewport)
public viewPort: CdkVirtualScrollViewport;
...
}
AppComponentTemplate with template reference passing of the ListComponent:
<p>Start editing to see some magic happen :)</p>
<input #inputText />
<button #addButton (click)="addItem(list)">Add New</button>
<list-container #list [data]="favoriteList" (removeData)="remove($event)">
</list-container>
AppComponent-> addItem():
addItem(listComp: ListComponent) {
const newItem = {
id: Number(this.input.nativeElement.value) + 1,
title: `item ${Number(this.input.nativeElement.value) + 1}`,
};
this.favoriteList = [newItem, ...this.favoriteList];
listComp.viewPort.scrollToIndex(0, 'smooth');
}
StackBlitz for the second solution:
https://stackblitz.com/edit/angular-ivy-ofhubv?file=src/app/app.component.html

How to make an event toggle HTML class on one element while removing it from others?

So, I have a little project that showcases a set of images, small-size. And when you click on one of them, it expands. Basically, JavaScript adds an HTML class of "active" to an element, which transforms it.
const panels = document.querySelectorAll('.panel');
/*adding an event for every image panel that makes
panel active on click */
panels.forEach(panel => {
panel.addEventListener('click', () => {
removeActiveClasses()
panel.classList.add('active')
})
})
//a function that removes active class from a panel
function removeActiveClasses() {
panels.forEach(panel => {
panel.classList.remove('active')
})
}
So, when you click on the element, it removes all the .active classes from every other one, and then adds it to a target element. It works perfectly, but I want to be able to delete a class from an element that is currently active, so when I click on an expanded picture, it collapses back. Changing panel.classList.add to panel.classList.toggle obviously doesn't work, because it first removes all active classes and then "toggles" it (or adds, because there is none). How can I delete a class from active element on click, while remaining the other functionality?
Solution: Thanks to CBroe I've managed to make it work. The code now looks like the following:
const panels = document.querySelectorAll('.panel');
/*adding an event for every image panel that makes
panel active on click */
panels.forEach(panel => {
panel.addEventListener('click', () => {
const isActive = panel.classList.contains('active')
removeActiveClasses()
panel.classList.toggle('active', !isActive)
})
})
//a function that removes active class from a panel
function removeActiveClasses() {
panels.forEach(panel => {
panel.classList.remove('active')
})
}
A much more efficient way would be to :
document.getElementsByClassName('panel').addEventListener('click', function() {
for (let ele of panels) {
ele.classList.remove('active')
}
let panel = this
panel.classList.includes('active') ? panel.classList.remove('active') : panel.classList.add('active')
)

How to select all tabs to the right of current tab?

If you right click on any tab at the top of Chrome browser, you will see an option called Close tabs to the right. This closes all tabs to the right of the current active tab. I'm trying to do something similar with a Chrome extension. Can the tabs to the right be selected using a loop like "for index of current active tab until index of last tab?"
Following is the source code of an open source Chrome extension. The function selects all tabs in the current window except for the active tab and "suspends" them. I am trying to write a similar function but instead of all tabs, it needs to select only the tabs to the right of the active tab.
function suspendAllTabs(force) {
const forceLevel = force ? 1 : 2;
getCurrentlyActiveTab(activeTab => {
if (!activeTab) {
gsUtils.warning(
'background',
'Could not determine currently active window.'
);
return;
}
chrome.windows.get(activeTab.windowId, { populate: true }, curWindow => {
for (const tab of curWindow.tabs) {
if (!tab.active) {
gsTabSuspendManager.queueTabForSuspension(tab, forceLevel);
}
}
});
});
Each tab has an index which shows its position. For example, the 3rd tab will have an index of 2 (starts from 0).
Therefore, tab to the right of the any tab means tab.index +1 to tabs.length
For example ...
Getting the tabs to the right of the active tab
// get all the tabs in current window
chrome.tabs.query({currentWindow: true}, tabs => {
let activeIndex;
for (const tab of tabs) {
// set the activeIndex so we wont have to run a loop on the tabs twice
if (tab.active) { activeIndex = tab.index; }
// tabs to the right of the active tab will have higher index
if(typeof activeIndex !== 'undefined' && tab.index > activeIndex) {
// tabs is on the right of the active tab ... do whatever needed
}
}
});
Getting the tabs to the left of the active tab
// get all the tabs in current window
chrome.tabs.query({currentWindow: true}, tabs => {
for (const tab of tabs) {
// stop when reached the active tab
if (tab.active) { break; }
// tabs to the left of the active tab ... do whatever needed
}
});
Another option which can be useful in many situations and is very intuitive is to use filtering to get your new tabs.
Adding to #erosman’s answer. When you get the tabs you can:
// All to right of current
tabs = tabs.filter((x)=> x.index > activeTab.index);
// All to left of current
tabs = tabs.filter((x)=> x.index < activeTab.index);
// Do whatever with the new tabs.
Similar approach can be taken to get any tab as long as the condition in the filter is met!

on item click, items re-renders and scroll comes to the top which is undesired

I have made a component where I am rendering grids of items. On clicking one item, the item is being selected. However there are many items present so there is scroll bar. Whenever I click on an Item, the component is re-rendered (as I am putting the selectedItem in my state), which further re-renders all the other items. But when I click an item after scrolling to the bottom (or middle), the component renders to the top, however I want that to remain on the position it was being clicked.
The components are as follows :
Full-Screen (made using react-portal, contains onClick and changes its state)
--TilesView (all tiles wrapper which renders all the tiles and has an ajax call)
--all Tiles (single tile element)
The part code is as follows :
FullScreen:
componentDidMount() {
if (this.props.selectedPost) {
this.setState({
selectedPost: {
[this.props.selectedPost[0]]: true
}
});
}
}
render() {
const that = this;
//Todo: User fullpage header when space is updated
return (
<Portal container={() => document.querySelector('body')}>
<div className={styles.container}>
<FullPageForm onHide={that.props.onCancel} closeIcnLabel={'esc'} bgDark={true}>
<FullPageForm.Body>
<span className={styles.header}>{'Select Post'}</span>
<div className={styles.body}>
<ExistingAssets onCreativeTileClicked={this.handlePostClick}
selectedCreatives={this.state.selectedPost}
showSelectedTick/>
</div>
</FullPageForm.Body>
</FullPageForm>
</div>
</Portal>
);
}
handlePostClick = (adCreativeAsset, id) => {
event.preventDefault();
this.setState({
selectedPost: {
[id]: adCreativeAsset
}
});
}
In my handlePostClick, I tried doing event.preventDefault() but it didn't work. I have no clue why this is happening, thanks in advance.
Try changing your handlePostClick definition to
handlePostClick = (e, adCreativeAsset, id) => {
e.preventDefault();
//blah blah what you want
}
and in your JSX change onCreativeTileClicked={this.handlePostClick} to onCreativeTileClicked={this.handlePostClick.bind(this)}.
The event you were prevent-defaulting (stopping propagation in real terms) isn't the real event coming from the click but a synthetic one that can be summoned to fill in for an event when there isn't one. You need to stop propagation for the real event.
Hope this helps.

jquery and multiple element hover check

I have 3 boxes and once user hovers any, if changes the content of the big main div from default to the related div via featVals hash table
At the if ($('#estate-feature, #carrier-feature, #cleaning-feature').is(':hover')) { part of my code, I want to check if any of these 3 div boxes are currently hovered, if not display the default content (defaultFeat variable).
However I am getting Uncaught Syntax error, unrecognized expression: hover error from Google Chrome Javascript Console.
How can I fix it ?
Regards
$('#estate-feature, #carrier-feature, #cleaning-feature').hover(function () {
var currentFeatCont = featVals[$(this).attr('id')];
headlineContent.html(currentFeatCont);
}, function () {
headlineContent.delay(600)
.queue(function (n) {
if ($('#estate-feature, #carrier-feature, #cleaning-feature').not(':hover')) {
$(this).html(defaultFeat);
}
n();
})
});
:hover isn't an attribute of the element. Also, you are binding to the hover out there so you know that you have left the hover and can restore the default content. If you want the hover-triggered content to remain for a period after the point has left the trigger element then you'll either need to assume that you aren't going to roll over another trigger or implement a shared flag variable that indicates if the default text restore should be halted. e.g.
var isHovered = false;
$('#estate-feature, #carrier-feature, #cleaning-feature').hover(
function() {
var currentFeatCont = featVals[$(this).attr('id')];
headlineContent.html(currentFeatCont);
isHovered = true;
},
function() {
isHovered = false;
headlineContent.delay(600)
.queue(function(n) {
if (!isHovered) {
$(this).html(defaultFeat);
}
n();
})
}
);

Categories