Error in Response to Tabs.getcurrent: Typeerror: Cannot Read Property 'id' of Undefined Extension
Working with the Tabs API
Tabs let a user open several spider web pages in their browser window and then switch between those web pages. With the Tabs API, yous can work with and manipulate these tabs to create utilities that provide users with new means to work with tabs or to deliver the features of your extension.
In this how-to article we'll wait at:
- Permissions needed to use the Tabs API.
- Discovering more almost tabs and their properties using
tabs.query
. - Creating, duplicating, moving, updating, reloading, and removing tabs.
- Manipulating a tab'southward zoom level.
- Manipulating a tab's CSS.
We then conclude by looking at some other, miscellaneous features offered by the API.
Note: In that location are some Tab API features covered elsewhere. These are the methods you tin use to manipulate tab content with scripts (tabs.connect
, tabs.sendMessage
, and tabs.executeScript
). If you want more than data on these methods, encounter the Concepts article Content scripts and the how-to guide Modify a web page.
Permissions and the Tabs API
For the bulk of the Tabs API functions you lot don't need any permissions; still, there are some exceptions:
-
"tabs
" permission is needed to access theTab.url
,Tab.title
, andTab.favIconUrl
properties of the Tab object. In Firefox, you too demand"tabs"
to perform a query by URL. - Host permission is needed for
tabs.executeScript()
ortabs.insertCSS()
.
The following is how you might request "tabs"
permission in your extension's manifest.json file:
"permissions" : [ "<all_urls>" , "tabs" ] ,
This asking gives y'all use of all Tabs API characteristic on all website your user visits. At that place is too an alternative approach for requesting permissions to employ tabs.executeScript()
or tabs.insertCSS()
where you don't need host permission, in the class of "activeTab"
. This permission provides the aforementioned rights as "tabs"
with <all_urls>
, but with two restrictions:
- the user must interact with the extension through its browser or page action, context menu, or shortcut key.
- it but grants permission within the active tab.
The benefit of this approach is the user won't get a permissions warning proverb your extension can "Access your data for all websites". This is considering <all_urls>
permission gives an extension the ability to execute scripts in any tab, any fourth dimension it likes, whereas "activeTab"
is limited to allowing the extension to perform a user requested action in the current tab.
Discovering more about tabs and their properties
At that place will exist occasions when yous want to get a list of all the tabs in all the browser windows. Other times you lot might want to discover a subset of tabs that match some specific criteria, such as those opened from a specific tab or displaying pages from a particular domain. And once you lot have your list of tabs, y'all'll probably want to know more than about their properties.
This is where tabs.query()
comes in. Used lone to get all tabs or taking the queryInfo
object—to specify query criteria such as whether the tab is active, in the current window, or ane or more of 17 criteria—tabs.query()
returns an array of tabs.Tab
objects containing information about the tabs.
Where you want data almost the current tab only, you tin get a tabs.Tab
object for that tab using tabs.getCurrent()
. If you accept a tab's ID, yous can get its tabs.Tab
object using tabs.get()
.
How to example
To run across how tabs.query()
and tabs.Tab
are used, let'southward walk through how the tabs-tabs-tabs case adds the list of "switch to tabs" to its toolbar button popup.
- manifest.json
-
Hither is the
manifest.json
:{ "browser_action" : { "browser_style" : true , "default_title" : "Tabs, tabs, tabs" , "default_popup" : "tabs.html" } , "description" : "A list of methods you can perform on a tab." , "homepage_url" : "https://github.com/mdn/webextensions-examples/tree/master/tabs-tabs-tabs" , "manifest_version" : 2 , "proper noun" : "Tabs, tabs, tabs" , "permissions" : [ "tabs" ] , "version" : "1.0" }
Note:
-
tabs.html
is defined as thedefault_popup
inbrowser_action
. It is displayed whenever the user clicks the extension'southward toolbar icon. - Permissions includes tabs. This is needed to support the tab list characteristic, as the extension reads the championship of the tabs for display in the popup.
-
- tabs.html
-
tabs.html
defines the content of the extension's popup:<! DOCTYPE html > <html > <head > <meta charset = "utf-eight" > <link rel = "stylesheet" href = "tabs.css" /> </caput > <body > <div class = "panel" > <div form = "panel-section panel-section-header" > <div class = "text-section-header" > Tabs-tabs-tabs </div > </div > <a href = "#" id = "tabs-movement-beginning" > Move active tab to the beginning of the window </a > <br > <!-- Define the other menu items --> <div class = "switch-tabs" > <p > Switch to tab </p > <div id = "tabs-list" > </div > </div > </div > <script src = "tabs.js" > </script > </body > </html >
This does the following:
- The menu items are declared.
- An empty
div
with the IDtabs-list
is declared to contain the list of tabs. -
tabs.js
is called.
- tabs.js
-
In
tabs.js
, we'll see how the listing of tabs is congenital and added to the popup.
Outset, an result handler is added to execute listTabs()
when tabs.html
is loaded:
certificate. addEventListener ( "DOMContentLoaded" , listTabs) ;
The offset thing that listTabs()
does is to call getCurrentWindowTabs()
. This is where tabs.query()
is used to get a tabs.Tab
object for the tabs in the current window:
part getCurrentWindowTabs ( ) { return browser.tabs. query ( { currentWindow : true } ) ; }
Now, listTabs()
is prepare to create the content for the popup.
To outset with:
- Grab the
tabs-list
div
. - Create a certificate fragment (into which the list volition be congenital).
- Gear up counters.
- Clear the content of the
tabs-list
div
.
role listTabs ( ) { getCurrentWindowTabs ( ) . then ( ( tabs ) => { permit tabsList = document. getElementById ( 'tabs-listing' ) ; let currentTabs = certificate. createDocumentFragment ( ) ; let limit = v ; let counter = 0 ; tabsList.textContent = '' ;
Next, we'll create the links for each tab:
- Loops through the first 5 items from the
tabs.Tab
object. - For each item, add a hyperlink to the document fragment.
- The link's characterization—that is, its text—is gear up using the tab's
title
(or theid
, if it has nochampionship
). - The link'due south address is set using the tab's
id
.
- The link's characterization—that is, its text—is gear up using the tab's
for ( let tab of tabs) { if ( !tab.agile && counter <= limit) { let tabLink = document. createElement ( 'a' ) ; tabLink.textContent = tab.title || tab.id; tabLink. setAttribute ( 'href' , tab.id) ; tabLink.classList. add ( 'switch-tabs' ) ; currentTabs. appendChild (tabLink) ; } counter += ane ; }
Finally, the certificate fragment is written to the tabs-list
div
:
tabsList. appendChild (currentTabs) ; } ) ; }
Working with the active tab
Some other related instance characteristic is the "Warning agile tab" info option that dumps all the tabs.Tab
object properties for the agile tab into an alarm:
else if (e.target.id === "tabs-alertinfo" ) { callOnActiveTab ( ( tab ) => { allow props = "" ; for ( let detail in tab) { props += ` ${ item } = ${ tab[item] } \n ` ; } warning (props) ; } ) ; }
Where callOnActiveTab()
finds the agile tab object by looping through the tabs.Tab
objects looking for the item with agile set:
document. addEventListener ( "click" , function ( e ) { function callOnActiveTab ( callback ) { getCurrentWindowTabs ( ) . then ( ( tabs ) => { for ( var tab of tabs) { if (tab.active) { callback (tab, tabs) ; } } } ) ; } }
Creating, duplicating, moving, updating, reloading, and removing tabs
Having gathered information virtually the tabs you'll about likely want to do something with them—either to offer users features for manipulating and managing tabs or to implement functionality in your extension.
The post-obit functions are bachelor:
- create a new tab (
tabs.create()
). - duplicate a tab (
tabs.duplicate()
). - remove a tab (
tabs.remove()
). - move a tab (
tabs.move()
). - update the tab's URL—finer browse to a new page—(
tabs.update()
). - reload the tab's page (
tabs.reload()
).
How to instance
The tabs-tabs-tabs example exercises all of these features except for updating a tab's URL The style in which these APIs are used is similar, so nosotros'll look at one of the more involved implementations, that of the "Move active tab to the first of the window list" option.
Only starting time, here is a demonstration of the feature in activity:
- manifest.json
-
None of the functions require a permission to operate, and then at that place are no features in the manifest.json file that need to be highlighted.
- tabs.html
-
tabs.html
defines the "carte du jour" displayed in the popup, which includes the "Movement active tab to the beginning of the window list" choice, with a series of<a>
tags grouped by a visual separator. Each menu item is given anid
, which is used intabs.js
to determine which menu particular is existence requested.<a href = "#" id = "tabs-move-showtime" > Motion active tab to the beginning of the window </a > <br > <a href = "#" id = "tabs-movement-end" > Move agile tab to the end of the window </a > <br > <div grade = "panel-department-separator" > </div > <a href = "#" id = "tabs-indistinguishable" > Duplicate active tab </a > <br > <a href = "#" id = "tabs-reload" > Reload active tab </a > <br > <a href = "#" id = "tabs-alertinfo" > Alert active tab info </a > <br >
- tabs.js
-
To implement the "carte" defined in
tabs.html
,tabs.js
includes a listener for clicks intabs.html
:document. addEventListener ( "click" , office ( e ) { function callOnActiveTab ( callback ) { getCurrentWindowTabs ( ) . then ( ( tabs ) => { for ( var tab of tabs) { if (tab.active) { callback (tab, tabs) ; } } } ) ; } }
A series of
if
statements then look to match theid
of the detail clicked.This lawmaking snippet is for the "Motility active tab to the commencement of the window list" option:
if (due east.target.id === "tabs-move-first" ) { callOnActiveTab ( ( tab, tabs ) => { var index = 0 ; if ( !tab.pinned) { index = firstUnpinnedTab (tabs) ; } console. log ( ` moving ${tab.id} to ${index} ` ) browser.tabs. movement ( [tab.id] , {index} ) ; } ) ; }
It's worth noting the utilize of
console.log()
. This enables y'all to output information to the debugger console, which can be useful when resolving issues found during development.The move code first calls
callOnActiveTab()
which in turn callsgetCurrentWindowTabs()
to get atabs.Tab
object containing the active window'southward tabs. It then loops through the object to find and return the active tab object:function callOnActiveTab ( callback ) { getCurrentWindowTabs ( ) . and so ( ( tabs ) => { for ( var tab of tabs) { if (tab.active) { callback (tab, tabs) ; } } } ) ; }
Pinned tabs
A feature of tabs is that the user can pin tabs in a window. Pinned tabs are placed at the start of the tab list and cannot exist moved. This means that the earliest position a tab can move to is the first position after any pinned tabs. So, firstUnpinnedTab()
is called to observe the position of the first unpinned tab by looping through the tabs
object:
function firstUnpinnedTab ( tabs ) { for ( let tab of tabs) { if ( !tab.pinned) { return tab.index; } } }
We now have everything needed to move the tab: the active tab object from which we tin can get the tab id
and the position the tab is to be moved to. So, nosotros can implement the motion:
browser.tabs. motility ( [tab.id] , {alphabetize} ) ;
The remaining functions to duplicate, reload, create, and remove tabs are implemented similarly.
Manipulating a tab's zoom level
The next set of functions enable y'all to get (tabs.getZoom
) and set (tabs.setZoom
) the zoom level inside a tab. Yous can likewise call back the zoom settings (tabs.getZoomSettings
) only, at the fourth dimension of writing, the ability to set the settings (tabs.setZoomSettings
) wasn't available in Firefox.
The level of zoom can be between 30% and 500% (represented every bit decimals 0.3
to 5
).
In Firefox the default zoom settings are:
- default zoom level: 100%.
- zoom mode: automatic (so the browser manages how zoom levels are set).
- scope of zoom changes:
"per-origin"
, meaning that when you visit a site again, it takes the zoom level set in your last visit.
How to case
The tabs-tabs-tabs example includes three demonstrations of the zoom characteristic: zoom in, zoom out, and reset zoom. Here is the feature in action:
Let'south take a look at how the zoom in is implemented.
- manifest.json
-
None of the zoom functions crave permissions, so in that location are no features in the manifest.json file that need to be highlighted.
- tabs.html
-
Nosotros have already discussed how the
tabs.html
defines the options for this extension, aught new or unique is done to provide the zoom options. - tabs.js
-
tabs.js
starts past defining several constants used in the zoom lawmaking:const ZOOM_INCREMENT = 0.2 ; const MAX_ZOOM = 5 ; const MIN_ZOOM = 0.three ; const DEFAULT_ZOOM = 1 ;
It so uses the same listener we discussed earlier and so it tin can act on clicks in
tabs.html
.For the zoom in feature, this runs:
else if (e.target.id === "tabs-add-zoom" ) { callOnActiveTab ( ( tab ) => { var gettingZoom = browser.tabs. getZoom (tab.id) ; gettingZoom. then ( ( zoomFactor ) => { //the maximum zoomFactor is five, information technology tin't get higher if (zoomFactor >= MAX_ZOOM ) { alert ( "Tab zoom cistron is already at max!" ) ; } else { var newZoomFactor = zoomFactor + ZOOM_INCREMENT ; //if the newZoomFactor is set to higher than the max accepted //information technology won't alter, and will never alert that it'south at maximum newZoomFactor = newZoomFactor > MAX_ZOOM ? MAX_ZOOM : newZoomFactor; browser.tabs. setZoom (tab.id, newZoomFactor) ; } } ) ; } ) ; }
This lawmaking uses
callOnActiveTab()
to get the details of the active tab, thentabs.getZoom
gets the tab's electric current zoom factor. The current zoom is compared to the defined maximum (MAX_ZOOM
) and an alert issued if the tab is already at the maximum zoom. Otherwise, the zoom level is incremented just limited to the maximum zoom, then the zoom is set withtabs.getZoom
.
Manipulating a tab'due south CSS
Another significant capability offered by the Tabs API is the ability to manipulate the CSS within a tab—add new CSS to a tab (tabs.insertCSS()
) or remove CSS from a tab (tabs.removeCSS()
).
This tin be useful, for instance, if you want to highlight certain page elements or alter the default layout of the page.
How to example
The apply-css example uses these features to add together a red border to the spider web page in the active tab. Here is the characteristic in activity:
Let's walk through how information technology'southward fix up.
- manifest.json
-
The
manifest.json
requests permissions required to use the CSS features. You need either:-
"tabs"
permission and host permission; or, -
"activeTab"
permission.
The latter is the most useful, every bit it allows an extension to use
tabs.insertCSS()
andtabs.removeCSS()
in the active tab when run from the extension's browser or page activeness, context menu, or a shortcut.{ "clarification" : "Adds a page action to toggle applying CSS to pages." , "manifest_version" : two , "proper name" : "apply-css" , "version" : "1.0" , "homepage_url" : "https://github.com/mdn/webextensions-examples/tree/master/use-css" , "background" : { "scripts" : [ "groundwork.js" ] } , "page_action" : { "default_icon" : "icons/off.svg" , "browser_style" : true } , "permissions" : [ "activeTab" , "tabs" ] }
You lot will note that
"tabs"
permission is requested in addition to"activeTab"
. This additional permission is needed to enable the extension's script to access the tab's URL, the importance of which nosotros'll meet in a moment.The other primary features in the manifest.json file are the definition of:
- a background script, which starts running as soon as the extension is loaded.
- a "page activeness", which defines an icon to exist added to the browser's address bar.
-
- background.js
-
On startup,
background.js
sets some constants to ascertain the CSS to be practical, titles for the "page action", and a list of protocols the extension will work in:const CSS = "trunk { border: 20px solid red; }" ; const TITLE_APPLY = "Apply CSS" ; const TITLE_REMOVE = "Remove CSS" ; const APPLICABLE_PROTOCOLS = [ "http:" , "https:" ] ;
When outset loaded, the extension uses
tabs.query()
to get a listing of all the tabs in the electric current browser window. Information technology then loops through the tabs callinginitializePageAction()
.var gettingAllTabs = browser.tabs. query ( { } ) ; gettingAllTabs. so ( ( tabs ) => { for ( let tab of tabs) { initializePageAction (tab) ; } } ) ;
initializePageAction
usesprotocolIsApplicable()
to decide whether the active tab'southward URL is i the CSS can be applied to:role protocolIsApplicable ( url ) { var ballast = certificate. createElement ( 'a' ) ; anchor.href = url; return APPLICABLE_PROTOCOLS . includes (ballast.protocol) ; }
Then, if the example can act on the tab,
initializePageAction()
sets the tab'southpageAction
(navigation bar) icon and championship to use the "off" versions before making thepageAction
visible:function initializePageAction ( tab ) { if ( protocolIsApplicable (tab.url) ) { browser.pageAction. setIcon ( { tabId : tab.id, path : "icons/off.svg" } ) ; browser.pageAction. setTitle ( { tabId : tab.id, title : TITLE_APPLY } ) ; browser.pageAction. prove (tab.id) ; } }
Next, a listener on
pageAction.onClicked
waits for thepageAction
icon to be clicked, and callstoggleCSS
when it is.browser.pageAction.onClicked. addListener (toggleCSS) ;
toggleCSS()
gets the championship of thepageAction
and and then takes the activeness described:- For "Employ CSS":
- toggles the
pageAction
icon and title to the "remove" versions. - applies the CSS using
tabs.insertCSS()
.
- toggles the
- For "Remove CSS":
- toggles the
pageAction
icon and title to the "use" versions. - removes the CSS using
tabs.removeCSS()
.
- toggles the
function toggleCSS ( tab ) { function gotTitle ( title ) { if (title === TITLE_APPLY ) { browser.pageAction. setIcon ( { tabId : tab.id, path : "icons/on.svg" } ) ; browser.pageAction. setTitle ( { tabId : tab.id, title : TITLE_REMOVE } ) ; browser.tabs. insertCSS ( { code : CSS } ) ; } else { browser.pageAction. setIcon ( { tabId : tab.id, path : "icons/off.svg" } ) ; browser.pageAction. setTitle ( { tabId : tab.id, title : TITLE_APPLY } ) ; browser.tabs. removeCSS ( { code : CSS } ) ; } } var gettingTitle = browser.pageAction. getTitle ( { tabId : tab.id} ) ; gettingTitle. and then (gotTitle) ; }
Finally, to ensure that the
pageAction
is valid after each update to the tab, a listener ontabs.onUpdated
callsinitializePageAction()
each time the tab is updated to check that the tab is withal using a protocol to which the CSS can be applied.browser.tabs.onUpdated. addListener ( ( id, changeInfo, tab ) => { initializePageAction (tab) ; } ) ;
- For "Employ CSS":
Another interesting abilities
At that place are a couple of other Tabs API features that don't fit into one of the before sections:
- Capture the visible tab content with
tabs.captureVisibleTab
. - Discover the primary linguistic communication of the content in a tab using
tabs.detectLanguage
. This could be used, for case, to lucifer the language in your extension's UI with that of the folio information technology's running in.
Learn more than
Source: https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Working_with_the_Tabs_API
0 Response to "Error in Response to Tabs.getcurrent: Typeerror: Cannot Read Property 'id' of Undefined Extension"
Post a Comment