Closing current tab from JavaScript
-
Following a discussion in another thread, I want to write custom JavaScript to add a toolbar button that closes current tab. Here's what I came up with:
function closeCurrentTab() { var div = document.createElement('div'); div.innerHTML = '<button class="close" title="Close tab" tabindex="-2"><svg width="18" height="18" viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg"><path d="M 13.5 6 l -1.4 -1.4 l -3.1 3 l -3.1 -3 L 4.5 6 l 3.1 3.1 l -3 2.9 l 1.5 1.4 L 9 10.5 l 2.9 2.9 l 1.5 -1.4 l -3 -2.9"/></svg></button>'; div.classList.add('button-toolbar', 'closeTab'); var trashButton = document.querySelector('.toggle-trash'); trashButton.parentNode.insertBefore(div, trashButton.nextSibling); document.querySelector('.closeTab button svg').style = 'width: 18px; height: 18px;'; document.querySelector('.closeTab').addEventListener('click', function(){ window.close(); }); };
This creates a close button next to trash icon (the one for restoring closed tabs):
It re-uses the close button class and SVG icon so it is consistent with close buttons displayed on the tabs. There's a catch though:
window.close()
in the'click'
callback does not work, so the button essentially does nothing. I searched the forums and found this topic, which suggests thatwindow.close()
function does not work in Vivaldi anymore. Is there a workaround for this? Can I somehow re-use the'click'
handler defined invendor-bundle.js
? This seems to be the handler that the close tab buttons displayed on tabs are calling. -
@killy9999
window.close
can only close a tab it created itself. There might be a workaround, but I would simply simulate the click on the active tab instead. Clicking buttons hidden with css should work, in case you don’t want to display the close button on every tab. Of course you can also hide the close buttons in settings and close the tab with simulated doubleclick. Calling the function to close a tab on the other hand, would probably require vivaldihooks. -
@luetage said:
Calling the function to close a tab on the other hand, would probably require vivaldihooks.
Why? We have
chrome.tabs
. One-liner from devtools:chrome.tabs.query({windowId:window.vivaldiWindowId, active:true}, t => { if (t[0] && t[0].id) chrome.tabs.remove(t[0].id) })
Of course you can use
chrome.windows.WINDOW_ID_CURRENT
instead ofwindow.vivaldiWindowId
, but I prefer that one. -
I came up with this:
var closeButton = document.querySelector('.active .tab-header button'); if ( closeButton ) { closeButton.click(); }
A subjective advantage over what @potmeklecbohdan suggested is that my code does not close the browser window when there is only one tab left, because that one last tab does not have a "close" button (hence a
null
check before a click). -
@luetage, I have a follow-up question, since my code is based on yours. The new button I'm adding with JavaScript is created correctly but when I enter and then exit full screen (e.g. watching a video) it disappears. I'm guessing that the code is only run on browser startup but then the tab bar is destroyed when entering full screen and the extra button is not added when the tab bar is reconstructed. How do I run the code every time after leaving fullscreen? I browsed available events but none seems like an appropriate on to plug in the code creating the button.
-
@killy9999 I think you’re looking for onFullscreen.
-
@killy9999 Share your current code, I’ll fix it for you. Wasn’t aware the tabbar gets programmatically removed on fullscreen, because unlike the panel and the statusbar the tabbar stays active when you choose not to display it.
-
This should solve the issue with the disappearing button after fullscreen:
(function () { function closeCurrentTab() { chrome.tabs.query({windowId:window.vivaldiWindowId, active:true}, tab => { if (tab[0] && tab[0].id) { chrome.tabs.remove(tab[0].id); } }); } function checkLast() { chrome.tabs.query({windowId:window.vivaldiWindowId}, all => { if (all.length > 1) { closeCurrentTab(); } }); } function createButton() { var div = document.createElement('div'); div.innerHTML = '<button class="close" title="Close tab" tabindex="-2"><svg width="18" height="18" viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg"><path d="M 13.5 6 l -1.4 -1.4 l -3.1 3 l -3.1 -3 L 4.5 6 l 3.1 3.1 l -3 2.9 l 1.5 1.4 L 9 10.5 l 2.9 2.9 l 1.5 -1.4 l -3 -2.9"/></svg></button>'; div.classList.add('button-toolbar', 'closeTab'); var trashButton = document.querySelector('.toggle-trash'); trashButton.parentNode.insertBefore(div, trashButton.nextSibling); document.querySelector('.closeTab button svg').style = 'width: 18px; height: 18px;'; document.querySelector('.closeTab').addEventListener('click', checkLast); } var appendChild = Element.prototype.appendChild; Element.prototype.appendChild = function () { if (arguments[0].tagName === 'DIV' && arguments[0].classList.contains('sync-and-trash-container')) { setTimeout(function() { createButton(); }.bind(this, arguments[0])); } return appendChild.apply(this, arguments); } })();
-
@luetage yes, that works! Thanks! This some serious magic BTW - at least for someone like me who never wrote any JavaScript. I'm puzzled by the
bind
.Also, the code you showed me earlier as an example (the one creating a button with random theme selection) used something like this:
setTimeout( function wait() { const browser = document.getElementById('browser'); if ( browser ) { /* whatever */ }, true); } else { setTimeout(wait, 300); } }, 300);
and I extended the whatever part with:
/* Disable double-click events in the tab bar. This prevent opening of new tabs on double-click and at the same time allows clicking the close-tab button rapidly. */ document.querySelector('#tabs-container').addEventListener('dblclick', function (event) { event.stopPropagation(); }
But now I see that this code also does not survive going to fullscreen. I tried adding it to the top-level anonymous function but apparently that's not the right way of doing it. I'm guessing this has to be somehow done by modifying the prototypes? How can I know what arguments are being passed to a prototype?
-
@killy9999 Code like this runs only whenever a new window is being created. Originally the code worked for the whole browser, because all hidden parts were being hidden with css. Nowadays Vivaldi removes certain parts of the browser after certain actions. This apparently includes the tab container when going to fullscreen. The solution provided intercepts the appending of the tabs container and passes it on with new arguments, in this case your mod.
-
Ok, that makes sense. It looks like I got everything working the way I want it to. Thanks again for your help!
-
Why not just middle-click a tab to close it?
-
Ppafflick moved this topic from Modifications on