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!
Related
I have Telerik Kendo TabStrip on my page with the following markup:
#(Html.Kendo().TabStrip().Name("FilterTabs")
.Animation(a =>
{
a.Enable(true);
a.Open(c =>
{
c.Expand(ExpandDirection.Vertical);
c.Fade(FadeDirection.In);
c.Duration(400);
});
a.Close(c =>
{
c.Reverse(true);
});
})
.Items(t => t.Add().Text("Simple Filter").Selected(true)
.ContentHtmlAttributes(new { #class = "edit-pool-tab", #style = "min-height: 100px;" })
.Content(#<partial name="_FilterSimple" for="#Model" />))
.Items(t => t.Add().Text("Extended Filter")
.ContentHtmlAttributes(new { #class = "edit-pool-tab", #style = "min-height: 100px;" })
.Content(#<partial name="_FilterExtended" for="#Model" />))
)
It behaves incorrectly in the following ways:
When the page is loaded, the content of tab 1 (simple filter) are shown (which is correct) but both tab headers have the unselected look.
If I click on tab 2, the tab 2 header will get the selected look, but both contents of tab 1 and tab 2 are shown.
Only after I click back on tab 1, and then back on tab 2, will I get the correct behavior - contents of tab 2 disappear, and then if I click on tab 2 again contents of tab 1 disappear. If I click on tab 1 first after the page loads, I get the correct behavior.
Any idea what's causing this and how to fix it?
EDIT: I was suspecting this might be caused by the animation setings, so I removed the whole Animation section. It didn't help.
Apparently the .Selected(true) part is wonky, but the following workaround did the trick:
jQuery(document).ready(function () {
var tabstrip = $("#FilterTabs").kendoTabStrip().data("kendoTabStrip");
tabstrip.select(0);
}
);
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.
So I'm creating a theme for WP, and the menu is acting up. I suppose the dropdowns somehow overlap in the back, sometimes when I hover over one item, its submenu opens, but as I move the curses towards its submenu, the submenu of the item next to it opens instead.
Example:
Any ideas?
EDIT:
I noticed that when I hover over a top-level menu item for more than 2 seconds and then move the curser towards the submenu, the glitch doesn't happen.
What I found after doing a lot of digging is that once I hover over a top-level menu item, there is a class being added to it by my theme (Divi), and when I move the curser to another, the class is removed, but with a certain delay, so when I hover over a new top-level menu item, the previously hovered one still has the class appended to it for about 1 second.
I found the following code in the theme's files and I assume it is to blame, however I tried changing the 200 to 0 and the delay is still taking place (I'm tracking the classes being added and removed as I hover using DevTools on Chrome):
window.et_pb_toggle_nav_menu = function($element, state, delay) {
if ( 'open' === state ) {
if ( ! $element.closest( 'li.mega-menu' ).length || $element.hasClass( 'mega-menu' ) ) {
$element.addClass( 'et-show-dropdown' );
$element.removeClass( 'et-hover' ).addClass( 'et-hover' );
}
} else {
var closeDelay = typeof delay !== 'undefined' ? delay : 200;
$element.removeClass( 'et-show-dropdown' );
$element.removeClass( 'et-touch-hover' );
setTimeout( function() {
if ( ! $element.hasClass( 'et-show-dropdown' ) ) {
$element.removeClass( 'et-hover' );
}
}, closeDelay );
}
};
Have you tried playing with the z-index on hover? Something like:
.item {
z-index: 0;
&:hover {
z-index: 1;
}
}
I'm guessing right now without the code to use as a reference.
There are two buttons on the website I'm creating. They are used to move a slideshow-type form. One moves the 'slideshow' forward, and the other moves it back.
Upon loading the webpage, you can click the 'Next' button successfully once to move the slideshow in the chosen direction. You can click the 'Next' button as many times as you want until you reach the end. However, switching to the 'Back' requires you to click twice before the action is carried out. You can now click the 'Back' button once to carry out the action. Clicking the 'Next' button now requires you to click twice, just like the 'Back' button was before.
HTML:
<div id="signupFormButtonWrapper">
<button id="signupFormBack" class="button">Back</button>
<button id="signupFormNext" class="button">Next</button>
</div>
JS:
$(function() {
var currentScreen = 1;
$('#signupFormNext').click(function() {
console.log('Moving screen forwards');
if (currentScreen < $('.signupSection').length) {
$('#signupInfo').css('margin-left',currentScreen*(-100/$('.signupSection').length)+'%');
currentScreen++;
} else {
console.log('Can\'t move');
}
});
$('#signupFormBack').click(function() {
console.log('Moving screen backwards');
if (currentScreen >= 1) {
currentScreen--;
$('#signupInfo').css('margin-left',currentScreen*(-100/$('.signupSection').length)+'%');
} else {
console.log('Can\'t move');
}
});
});
Do excuse the console.log statements, they are for debugging purposes.
If it helps, when the buttons are clicked, the console does log 'Moving screen forwards' or 'Moving screen backwards', whether or not an action is carried out.
If you need more code, just ask.
Thanks in advance
Hmm, there's something wrong with the logic. Every time when you click the next button, the old value of currentScreen is used to calculate the left margin.
For example, you are on screen 4 moving to screen 5, 4 will be used for the calculation and then currentScreen will be updated to 5.
If you click back at this point, the currentScreen will be decremented from 5 to 4, and 4 will be used for the calculation again, which will result in the same value as previously calculated when clicking the next button (therefore no change). Hence, requiring you to click two times before the value of currentScreen gets decremented to 3.
Right and now after clicking the back button 2 times, currentScreen is 3, clicking the next button will cause 3 to be used for the calculation and the value for the margin left will be the same as the previous value. Therefore, requiring you 2 clicks before you see the screen move to the next slide.
You seem to increment current screen after you move, but decrement current screen before you move the screen. So let's work through the logic (state tracks the currentScreen variable (1 on start) and the slide image (0 on start))
STATE: currentScreen = 1, slide-image = 0
ACTION: Next button pressed
WHAT HAPPENS:
slide-image -> 1
currentScreen -> 2
STATE: currentScreen = 2, slide-image = 1
ACTION: Next button pressed
WHAT HAPPENS:
slide-image -> 2
currentScreen -> 3
So that works fine, now what happens when we press back
STATE: currentScreen = 3, slide-image = 2
ACTION: Prev button pressed
WHAT HAPPENS:
currentScreen -> 2
slide-image -> 2
STATE: currentScreen = 2, slide-image = 2
ACTION: Prev button pressed
WHAT HAPPENS:
currentScreen -> 1
slide-image -> 1
Here you can see, because you decrement before you set, there's a lag. The same thing happens again when you switch direction. To fix this problem start the counter at 0 and then decrement/increment before you set the image each way. eg:
$(function() {
var currentScreen = 0; // start at 0
$('#signupFormNext').click(function() {
console.log('Moving screen forwards');
if (currentScreen < $('.signupSection').length) {
currentScreen++; // increment before you set css
$('#signupInfo').css('margin-left',currentScreen*(-100/$('.signupSection').length)+'%');
} else {
console.log('Can\'t move');
}
});
$('#signupFormBack').click(function() {
console.log('Moving screen backwards');
if (currentScreen >= 1) {
currentScreen--;
$('#signupInfo').css('margin-left',currentScreen*(-100/$('.signupSection').length)+'%');
} else {
console.log('Can\'t move');
}
});
});
I think you logic has an issue.
See the following jsfiddle: https://jsfiddle.net/q8pby5cL/1/
I abstracted the movement into a function. I replaced the check on the .signupSection with a hardcoded value for testing.
I'm using Dojo's Dijit Layout for generating content tab-panes similar to Dijit Theme Tester Demo. All of the tabs here are closeable.
The issue is: when I close a tab it goes back to the first tab in the list instead of the previous tab (available next to it).
You can think of it like opening a new tab in Firefox or Chrome and try closing the last tab.... on closing tab, it changes the focus to the previous tab which is a predictable behavior for working with tabs. But with dijit.TabContainers, by default it goes back to the very first tab instead of previous one. This is a serious flaw when you consider the UI basics.
I have checked with dojo docs, but don't found any hint on this. Any idea how to it?
Ok so when the [X] button on the dijit.layout.ContentPane (tab) is clicked an event onClose is generated, the dijit.layout.TabContainer is listening to this event, so when this happens, it executes the callback closeChild() then the function removeChild() is executed, this last one is the one you should override.
The tabContainer inherits this two functions from dijit.layout.StackContainer you should check the API documentation.
So for being able to modify the default behavior of the closing tabs, you should override the default functionality, in the example below i do this. Read the comments for info on where to add your logic.
E.g.
<script>
require([
"dojo/parser",
"dojo/_base/lang", //this is the one that has the extend function
"dojo/topic", //this is needed inside the overrided function
"dijit/layout/TabContainer",
"dijit/layout/ContentPane",
"dojo/domReady!"
], function(Parser, lang, topic, tabContainer, contentPane){
Parser.parse();
// this will extend the tabContainer class and we will override the method in question
lang.extend(tabContainer, {
// this function, i copied it from the dijit.layout.StackContainer class
removeChild: function(/*dijit._Widget*/ page){
// for this to work i had to add as first argument the string "startup"
this.inherited("startup", arguments);
if(this._started){
// also had to call the dojo.topic class in the require statement
topic.publish(this.id + "-removeChild", page);
}
if(this._descendantsBeingDestroyed){ return; }
if(this.selectedChildWidget === page){
this.selectedChildWidget = undefined;
if(this._started){
var children = this.getChildren();
if(children.length){
// this is what you want to add your logic
// the var children (array) contains a list of all the tabs
// the index selects the tab starting from left to right
// left most being the 0 index
this.selectChild(children[0]);
}
}
}
if(this._started){
this.layout();
}
}
});
// now you can use your modified tabContainer WALAAAAAAA!
// from here on, normal programmatic tab creation
var tc = new tabContainer({
style: "height: 100%; width: 100%;",
}, "tab-container-div-id");
var cp1 = new contentPane({
title: "First Tab",
closable: true
});
tc.addChild(cp1);
var cp2 = new contentPane({
title: "Second Tab",
closable: true
});
tc.addChild(cp2);
var cp3 = new contentPane({
title: "Third Tab",
selected: true,
closable: true
});
tc.addChild(cp3);
tc.startup();
});
</script>
In Dojo 1.10, reverting to the previous tab is the normal behaviour for TabContainers (instead of reverting to the first tab).
Presumably, you could use dojo/aspect to get the old behaviour (warning: not tested):
require( [ 'dijit/registry', 'dojo/aspect', 'dojo/_base/lang' ],
function( registry, aspect, lang )
{
var tabContainer = registry.byId( 'tab_container');
aspect.before( tabContainer, 'removeChild', lang.hitch( tabContainer, function( page )
{
if(this.selectedChildWidget === page)
{
this.selectedChildWidget = undefined;
if(this._started)
{
var children = this.getChildren();
this.selectChild( children[0] );
}
}
return page;
} ) );
}
);
Or, alternatively, you could use the onClose extension point on a tab's ContentPane:
require( [ 'dijit/registry', 'dojo/_base/lang' ],
function( registry, lang ) {
newTabPane.onClose = lang.hitch(this, function () {
// select first
var tabContainer = registry.byId('tab_container'),
all_tabs = tabContainer.getChildren();
tabContainer.selectChild( all_tabs[0] );
// allow save to go ahead
return true;
});
}
);
Obviously, both these approaches would allow you to select a specific different pane on a tab being closed instead with a little tweaking...