Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
您2019豬事順利,心想事成。
Tab 切換是種很常見(jiàn)的網(wǎng)頁(yè)呈現(xiàn)形式,不管是PC或者H5都會(huì)經(jīng)常看到,今天就為小伙伴們提供多種純CSS Tab 切換的實(shí)現(xiàn)方式,同時(shí)對(duì)比一下那種代碼更方便,更通俗易懂。
3種純CSS方式實(shí)現(xiàn)Tab 切換
純CSS實(shí)現(xiàn)都面臨2個(gè)問(wèn)題:
1、 如何接收點(diǎn)擊事件?
2、 如何操作相關(guān)DOM?
擁有 checked 屬性的表單元素, <input type="radio"> 或者 <input type="checkbox"> 能夠接收到點(diǎn)擊事件。
知識(shí)點(diǎn):
1、 使用 radio 標(biāo)簽的 :checked 偽類,加上 <label for> 實(shí)現(xiàn)純 CSS 捕獲點(diǎn)擊事情
2、 使用了 ~ 選擇符對(duì)樣式進(jìn)行控制
<div class="container"> <input class="nav1" id="li1" type="radio" name="nav"> <input class="nav2" id="li2" type="radio" name="nav"> <ul class='nav'> <li class='active'><label for="li1">tab1</label></li> <li><label for="li2">tab2</label></li> </ul> <div class="content"> <div class="content1 default">tab1 內(nèi)容:123456</div> <div class="content2">tab2 內(nèi)容:abcdefgkijkl</div> </div> </div>
添加樣式
.container *{ padding: 0; margin: 0; } .container { position: relative; width: 400px; margin: 50px auto; } .container input { display: none; } .nav { position: relative; overflow: hidden; } .nav li { width: 200px; float: left; text-align: center; background: #ddd; list-style: none; } .nav li label { display: block; width: 200px; line-height: 36px; font-size: 18px; cursor: pointer; } .content { position: relative; overflow: hidden; width: 400px; height: 100px; border: 1px solid #999; box-sizing: border-box; padding: 10px; } .content1, .content2 { display: none; width: 100%; height: 100%; } .nav1:checked ~ .nav li { background: #ddd; color: #000; } .nav1:checked ~ .nav li:first-child { background: #ff7300; color: #fff; } .nav2:checked ~ .nav li { background: #ddd; color: #000; } .nav2:checked ~ .nav li:last-child { background: #ff7300; color: #fff; } .nav1:checked ~ .content > div { display: none; } .nav1:checked ~ .content > div:first-child { display: block; } .nav2:checked ~ .content > div { display: none; } .nav2:checked ~ .content > div:last-child { display: block; } .nav li.active { background: #ff7300; color: #fff; } .content .default { display: block; }
知識(shí)點(diǎn):
1、 要使用 :target 偽元素,需要 HTML 錨點(diǎn),以及錨點(diǎn)對(duì)應(yīng)的 HTML 片段
2、 核心是使用 :target 偽類接收點(diǎn)擊事件
3、 通過(guò)兄弟選擇符 ~ 控制樣式
<div class="container"> <div id="content1" class="active">tab 1內(nèi)容:123456</div> <div id="content2">tab 2內(nèi)容:abcdefgkijkl</div> <ul class='nav'> <li class="active"><a href="#content1">tab1</a></li> <li><a href="#content2">tab2</a></li> </ul> <div class="wrap"></div> </div>
添加樣式
.container *{ padding: 0; margin: 0; } .container { position: relative; width: 400px; margin: 50px auto; } .nav { position: relative; overflow: hidden; } li { width: 200px; float: left; text-align: center; background: #ddd; list-style: none; } li a { display: block; width: 200px; line-height: 36px; font-size: 18px; cursor: pointer; text-decoration: none; color: #000; } #content1, #content2 { position: absolute; overflow: hidden; top: 36px; width: 400px; height: 100px; border: 1px solid #999; box-sizing: border-box; padding: 10px; } #content1, #content2 { display: none; width: 100%; background: #fff; } #content1:target, #content2:target { display: block; } #content1.active { display: block; } .active ~ .nav li:first-child { background: #ff7300; color: #fff; } #content1:target ~ .nav li { background: #ddd; color: #000; } #content1:target ~ .nav li:first-child { background: #ff7300; color: #fff; } #content2:target ~ .nav li { background: #ddd; color: #000; } #content2:target ~ .nav li:last-child { background: #ff7300; color: #fff; } .wrap { position: absolute; overflow: hidden; top: 36px; width: 400px; height: 100px; border: 1px solid #999; box-sizing: border-box; }
:focus-within 它表示一個(gè)元素獲得焦點(diǎn),或該元素的后代元素獲得焦點(diǎn)。
重點(diǎn):它或它的后代獲得焦點(diǎn)。
這也就意味著,它或它的后代獲得焦點(diǎn),都可以觸發(fā) :focus-within。
知識(shí)點(diǎn)
1、 這個(gè)屬性有點(diǎn)類似 Javascript 的事件冒泡,從可獲焦元素開(kāi)始一直冒泡到根元素 html,都可以接收觸發(fā) :focus-within 事件
2、 本例子的思路就是通過(guò)獲焦態(tài)來(lái)控制其他選擇器,以及最重要的是利用了父級(jí)的 :not(:focus-within) 來(lái)設(shè)置默認(rèn)樣式
<div class="container"> <div class="nav-box"> <button class="nav1">tab1</button> <button class="nav2">tab2</button> <div class="content-box"> <div class="content1"> content-1 </div> <div class="content2"> content-2 </div> </div> </div> </div>
添加樣式
.container { width: 300px; margin: 50px auto; padding: 10px; boder: 1px solid #ddd; } .nav-box { font-size: 0; } button { width: 150px; height: 40px; box-sizing: border-box; outline: none; background: #fff; border: 1px solid #ddd; font-size: 18px; cursor: pointer; } button:focus-within { color: #fff; background: #ff7300; } .content-box { font-size: 24px; border: 1px solid #ddd; height: 100px; } .content-box div { display: none; } .nav-box:not(:focus-within) .nav1 { color: #fff; background: #ff7300; } .nav-box:not(:focus-within) .content1 { display: block; } .nav1:focus-within ~ .content-box .content1 { display: block; } .nav2:focus-within ~ .content-box .content2 { display: block; }
3種純CSS方式實(shí)現(xiàn)Tab 切換
這個(gè)效果就很差一些,因?yàn)椋趖ab失去焦點(diǎn)時(shí),就會(huì)復(fù)原,回到tab1上面,并不推薦這種方式來(lái)實(shí)現(xiàn)。小編推薦第一種:checked實(shí)現(xiàn)方式,更容易理解。
喜歡小編的點(diǎn)擊關(guān)注,了解更多知識(shí)!
源碼地址和源文件下載請(qǐng)點(diǎn)擊下方“了解更多”
者:中國(guó)社會(huì)科學(xué)院世界經(jīng)濟(jì)與政治研究所副研究員 彭成義
近日,美國(guó)《反海外腐敗法》(又譯為《反海外賄賂法》)因《美國(guó)陷阱》一書(shū)而“名聲大噪”。這部由法國(guó)電力巨頭阿爾斯通前高管皮耶魯齊所著的新書(shū),通過(guò)講述切身經(jīng)歷及詳實(shí)考證,揭露了美國(guó)如何利用其霸權(quán)優(yōu)勢(shì)及“長(zhǎng)臂管轄”手段打擊美國(guó)企業(yè)海外競(jìng)爭(zhēng)對(duì)手的秘密。如今,在中美貿(mào)易摩擦的大背景下,美國(guó)又對(duì)我國(guó)電信巨擘華為故伎重演,并要求加拿大將正在溫哥華轉(zhuǎn)機(jī)的華為首席財(cái)務(wù)官孟晚舟進(jìn)行了扣留。這使得國(guó)人對(duì)于美國(guó)《反海外腐敗法》及霸凌行徑更加關(guān)注并引發(fā)熱議。由此,有必要深入討論美國(guó)《反海外腐敗法》及其濫用情況。
美國(guó)《反海外腐敗法》的出臺(tái)與演變
美國(guó)《反海外腐敗法》的出臺(tái),有一個(gè)重要的背景和政治誘因,即1977年“水門事件”。“水門事件”的發(fā)生,使美國(guó)高官和大企業(yè)主管這些傳統(tǒng)上受人尊重的上層階層的誠(chéng)信度遭到社會(huì)質(zhì)疑。媒體借機(jī)掀起揭開(kāi)黑幕運(yùn)動(dòng),社會(huì)各界開(kāi)始普遍要求加強(qiáng)對(duì)政府官員和大企業(yè)行為的監(jiān)督,官方被迫啟動(dòng)相關(guān)調(diào)查。隨之,美國(guó)證券交易委員會(huì)一份報(bào)告披露,美國(guó)有400多家公司在海外存在非法或者問(wèn)題交易,引發(fā)社會(huì)震動(dòng)。這直接導(dǎo)致美國(guó)國(guó)會(huì)當(dāng)年便以絕對(duì)優(yōu)勢(shì)通過(guò)《反海外腐敗法》,旨在打擊美國(guó)企業(yè)在國(guó)外賄賂行為,重建公眾對(duì)于美國(guó)商業(yè)系統(tǒng)的信心。
《反海外腐敗法》早期的實(shí)施效果并不理想,因?yàn)樗陀^上削弱了美國(guó)公司在海外市場(chǎng)的競(jìng)爭(zhēng)力,遭到美國(guó)企業(yè)界一定程度的抵制,尤其是那些原本可以把行賄支出計(jì)入商業(yè)成本而獲得稅收優(yōu)惠的公司,對(duì)此更是怨聲載道。由此,美國(guó)分別于1988、1994、1998年三次對(duì)《反海外腐敗法》進(jìn)行修訂,以更適應(yīng)國(guó)際市場(chǎng)的現(xiàn)實(shí)情況,同時(shí)大力推進(jìn)《反海外腐敗法》的國(guó)際化。具體而言,修改后的《反海外腐敗法》主要有幾個(gè)變化。
第一,對(duì)國(guó)際商業(yè)中的一些費(fèi)用,比如用以促進(jìn)外國(guó)政府機(jī)構(gòu)加快履行日常政府活動(dòng)的小額支出即“潤(rùn)滑費(fèi)”,進(jìn)行了合法化處理。
第二,擴(kuò)大了該法的適用對(duì)象,從先前的美國(guó)公司和個(gè)人,擴(kuò)展到和美國(guó)有一定關(guān)系的外國(guó)企業(yè)、自然人或者母公司等。只要在美國(guó)上市、使用美元結(jié)算,或者僅僅通過(guò)設(shè)在美國(guó)的服務(wù)器(如谷歌郵箱或微軟郵箱)收發(fā)、存儲(chǔ)(甚至只是過(guò)境)電子郵件促成腐敗行為發(fā)生,這些都被視為利用了美國(guó)的國(guó)際貿(mào)易工具而被納入該法適用范圍。
第三,要求美國(guó)總統(tǒng)采取行動(dòng),促成其他國(guó)家出臺(tái)與《反海外腐敗法》類似的法律。同時(shí),在美國(guó)霸權(quán)的極力推動(dòng)下,《反海外腐敗法》國(guó)際化取得了重要進(jìn)展。加拿大一些國(guó)家就出臺(tái)類似《反海外腐敗法》的國(guó)內(nèi)法。經(jīng)濟(jì)合作與發(fā)展組織(OECD)則也于1997年頒布《國(guó)際商業(yè)交易活動(dòng)反對(duì)行賄外國(guó)公職人員公約》。
腐敗是阻礙人類社會(huì)發(fā)展的一大毒瘤。特別是在當(dāng)今全球深度一體化的背景下,沒(méi)有國(guó)家能夠獨(dú)善其身,必須團(tuán)結(jié)起來(lái)共同致力于反腐。《反海外腐敗法》本是美國(guó)單方面的國(guó)際反腐行動(dòng),但鑒于美國(guó)在世界體系中的重要地位,其影響逐漸擴(kuò)大,在一定程度上推動(dòng)全球反腐實(shí)現(xiàn)了從無(wú)到有,并逐漸演變成一個(gè)全球腐敗治理機(jī)制。
當(dāng)然,我們也必須認(rèn)識(shí)到,美國(guó)制定該法律并在國(guó)際層面積極推廣之,并非全然出于反腐倡廉的道德境界,還有為與蘇聯(lián)爭(zhēng)霸謀求道德高地的戰(zhàn)略考量。換言之,美國(guó)“高尚”行為背后也摻雜著與蘇聯(lián)爭(zhēng)霸的私心雜念。而這也為蘇聯(lián)解體后,美國(guó)濫用《反海外腐敗法》為其私利服務(wù)留下了伏筆。
美國(guó)《反海外腐敗法》被霸權(quán)濫用
近年來(lái),美國(guó)《反海外腐敗法》越來(lái)越被霸權(quán)濫用。首先,雖然民粹主義可以利用反腐旗號(hào)上臺(tái),卻并非真心反腐,反而往往使腐敗形勢(shì)更加惡化。比如,印度的第一個(gè)反腐敗政黨“普通人黨”,在2015年新德里的地方選舉中異軍突起,一舉拿下70個(gè)立法會(huì)議席中的67個(gè)。但是好景不長(zhǎng),很快該黨就因?yàn)閮?nèi)斗、骯臟政治以及機(jī)制失調(diào),而導(dǎo)致公眾倍感失望。意大利的著名右翼民粹主義政黨也因卷入腐敗丑聞而遭受沉重打擊,導(dǎo)致該黨在2017年的中期選舉中丟掉主要大城市的執(zhí)政權(quán)。法國(guó)最大的民粹主義政黨——國(guó)民陣線的主席小勒龐,也因讓其保鏢及助理等非歐洲議會(huì)工作人員在歐洲議會(huì)領(lǐng)“空餉”,而被法國(guó)司法機(jī)關(guān)立案調(diào)查,并有10余名“國(guó)民陣線”議員涉入此案。
其次,美國(guó)總統(tǒng)特朗普本身已成為廉政高風(fēng)險(xiǎn)的重點(diǎn)監(jiān)視對(duì)象,其反腐舉措更多的是一種“美國(guó)優(yōu)先”的手段。作為民粹主義興起的代表,特朗普雖然藉著反腐的緣由上臺(tái),在競(jìng)選期間也反復(fù)發(fā)出要抽干華盛頓腐敗沼澤的豪言壯語(yǔ),但是其許多舉措似乎正在開(kāi)反腐的倒車。而且,特朗普也因其巨大的商業(yè)帝國(guó)背景和聯(lián)系,成為廉政高風(fēng)險(xiǎn)的重點(diǎn)監(jiān)視對(duì)象。國(guó)外在這方面的討論很多,甚至有的民間組織還每月發(fā)布特朗普腐敗風(fēng)險(xiǎn)監(jiān)督報(bào)告,梳理分析所有可能指控特朗普涉嫌腐敗的法律條款等。
不少人認(rèn)為,特朗普總統(tǒng)及其行政當(dāng)局某些做法不僅不合乎倫理,而且存在違法行為。特朗普就職不久,華盛頓的一家非盈利組織——“責(zé)任與倫理公民”,就正式向地方法院提起了針對(duì)特朗普的訴訟,控告特朗普違反了美國(guó)憲法中規(guī)定的“外國(guó)收益條款”。2017年6月中旬,華盛頓特區(qū)總檢察長(zhǎng)卡爾·拉辛及馬里蘭州總檢察長(zhǎng)布萊恩·弗洛西也對(duì)特朗普提起訴訟,指控他存在“史無(wú)前例的違憲行為”。該訴訟還宣稱,特朗普所繼續(xù)擁有的一個(gè)全球性的商業(yè)帝國(guó)已使總統(tǒng)“深深的陷入一個(gè)由國(guó)外及國(guó)內(nèi)政府行動(dòng)者組成的集團(tuán)中”,并且破壞了美國(guó)的政治體系的廉潔性。
早在2012年,特朗普就公開(kāi)抨擊《反海外腐敗法》為“荒謬和糟糕的法律”。不少觀察家擔(dān)心特朗普會(huì)削弱《反海外腐敗法》。事實(shí)上,特朗普和共和黨領(lǐng)導(dǎo)的國(guó)會(huì)已經(jīng)放棄奧巴馬政府在打擊石油和天然氣行業(yè)反賄賂的努力,美國(guó)內(nèi)務(wù)部最近也采取行動(dòng)停止了對(duì)《采掘業(yè)透明倡議》的執(zhí)行。美國(guó)退出TPP也被不少人認(rèn)為是美國(guó)反腐的倒退和損失,因?yàn)槠渲杏袑iT的透明與反腐條款將反腐與貿(mào)易聯(lián)系起來(lái),而這耗掉了不少廉政工作者的努力和心血。而且,更令人擔(dān)憂的是,特朗普的腐敗表象以及抵制防止利益沖突舉措的態(tài)度極可能為國(guó)內(nèi)的其他腐敗官員所效仿。
總之,不管是在美國(guó)國(guó)內(nèi)還是國(guó)外,美國(guó)政府過(guò)去幾十年苦心經(jīng)營(yíng)起來(lái)的廉潔領(lǐng)袖形象似乎在特朗普當(dāng)選后短暫的時(shí)間里出現(xiàn)了快速的崩塌。開(kāi)反腐倒車的特朗普政府,無(wú)疑已經(jīng)使以廉潔模范自居的美國(guó)開(kāi)始成為反腐的笑柄。當(dāng)然,這對(duì)全球反腐事業(yè)也構(gòu)成一定的沖擊。
然而,就是這樣一位對(duì)反腐不屑一顧的總統(tǒng),卻在其任上開(kāi)始大肆啟動(dòng)對(duì)外國(guó)企業(yè)涉嫌賄賂的處罰,力度之大、金額之高令人訝然。這從下圖2016-2018年的黃色部分所代表的對(duì)外處罰金額就可見(jiàn)一斑。
圖1:美國(guó)通過(guò)《反海外腐敗法》對(duì)企業(yè)實(shí)體的處罰一覽(圖表引自http://fcpa.stanford.edu/statistics-analytics.html?tab=2,2019年6月9日訪問(wèn))
啟示
第一,我們應(yīng)該客觀看待美國(guó)《反海外腐敗法》的歷史功績(jī)。不容否認(rèn),美國(guó)制定和推廣該法對(duì)于形成今天全球腐敗治理有著重要的推動(dòng)作用。打造全球廉潔的從商環(huán)境無(wú)疑仍然具有感召力,并應(yīng)成為世界各國(guó)共同努力的目標(biāo)。
第二,對(duì)于美國(guó)利用其霸權(quán)優(yōu)勢(shì)及長(zhǎng)臂管轄手段打擊國(guó)外競(jìng)爭(zhēng)企業(yè)應(yīng)該引起世人的高度重視和清醒認(rèn)識(shí)。事實(shí)上,不僅是在利用《反海外腐敗法》方面,當(dāng)前特朗普政府的“美國(guó)優(yōu)先”戰(zhàn)略正在將其過(guò)去幾十年辛辛苦苦營(yíng)造的秩序和制度所葬送。
第三,中國(guó)或可扛起促進(jìn)全球腐敗治理的大旗。一方面注重發(fā)揮《反海外腐敗法》對(duì)于國(guó)際治理的積極促進(jìn)作用,另一方面帶動(dòng)全球堅(jiān)決抵制其被某些霸權(quán)私用濫用。這對(duì)于進(jìn)一步提升我國(guó)在全球舞臺(tái)的形象,提高中國(guó)智慧和中國(guó)方案的感召力,并維護(hù)世界的公平與正義,都有重要的意義。
本文主要的內(nèi)容是幫助讀者朋友梳理chrome插件的tabs能力,如果您是第一次閱讀本文,也建議您在閱讀完本文后,嘗試看看我下面的這些系列文章,它們可以更好的幫您認(rèn)識(shí)和了解chrome插件:
一款瀏覽器插件具備非常強(qiáng)大的能力,它不僅可以向當(dāng)前所有的站點(diǎn)里注入腳本,對(duì)網(wǎng)站的功能進(jìn)行擴(kuò)展。更重要的是它還可以和瀏覽器的標(biāo)簽系統(tǒng)進(jìn)行交互,從而創(chuàng)建、修改、管理每一個(gè)tab,而這一切都基于插件系統(tǒng)為我們提供的tabs相關(guān)的API,chrome不僅提供了我們用于操作和管理tabs的API,而且還提供了和content腳本之間的通信方法。
溫馨提示:
Tabs API 只能在background腳本中以及option腳本、popup腳本、由chrome創(chuàng)建的tab中訪問(wèn)到,在content腳本中是無(wú)法訪使用的。
換句話說(shuō),chrome有選擇性的給不同的腳本環(huán)境注入了不同的chrome對(duì)象,導(dǎo)致提供的API具備差異性。
該圖是我們?cè)谔囟ōh(huán)境下可以通過(guò)chrome.tabs訪問(wèn)的所有的api,這些就是chrome為我們內(nèi)置的提供給開(kāi)發(fā)者的能力
在之前的文章中我們提到過(guò),如果要使用某些特別的API,我們需要在插件配置文件manifest.json中配置相應(yīng)的權(quán)限聲明,但幸運(yùn)的是對(duì)于tabs相關(guān)的部分API不需要在manifest.json中顯式的配置“tabs”就可以直接使用。比如說(shuō): 創(chuàng)建一個(gè)新的tab,重新加載某個(gè)tab,或者導(dǎo)航到另外一個(gè)URL等等。
但是下面的這些API在使用的時(shí)候,則需要加上相關(guān)的配置才可以使用,比如說(shuō):
chrome.tabs.executeScript() // 注入一段腳本并執(zhí)行
chrome.tabs.insertCss() // 注入一段css樣式
chrome.tabs.removeCss() // 移除一段css樣式
{
// manifest.json "host_permissions":[ "<all_urls>" ] // 支持正則匹配正則
}
接下來(lái)我們一一通過(guò)案例來(lái)認(rèn)識(shí)他們,從而感受每一個(gè)API的具體行為以及他們的使用條件、注意事項(xiàng)等等。
我們可以通過(guò)這個(gè)API來(lái)創(chuàng)建一個(gè)新的tab,這個(gè)tab和普通的站點(diǎn)不一樣,屬于插件所屬的頁(yè)面,因此支持跨域請(qǐng)求、獲取更多的chrome提供的方法。
// background.js
chrome.runtime.onInstalled.addListener(({reason})=> {
if (reason==='install') {
chrome.tabs.create({
url: "newtab.html" // 相對(duì)于background腳本的路徑下需要有一個(gè)newtab.html文件
});
}
});
上面的腳本意味著在插件第一次安裝完成之后,就會(huì)立馬創(chuàng)建一個(gè)新的標(biāo)簽頁(yè),所以如果我們想要在任何時(shí)候創(chuàng)建一個(gè)新的tab,就可以使用這個(gè)API,行業(yè)內(nèi)很多插件的工作臺(tái)都是創(chuàng)建一個(gè)新的tab頁(yè)進(jìn)行工作的,比如著名的代理插件SwitchySharp
該api默認(rèn)支持,不需要額外的manifest配置
我們可能有這樣的需要,獲取當(dāng)前瀏覽器窗口處于激活狀態(tài)的tab頁(yè)面,因?yàn)閷?duì)于同一個(gè)窗口,有且只有會(huì)一個(gè)tab是展示在用戶面前的,我們把這樣的tab稱為激活狀態(tài),這個(gè)時(shí)候我們就需要用到下面的api。
async function getCurrentTab() { let queryOptions={ active: true }; let [tab]=await chrome.tabs.query(queryOptions); return tab; }
調(diào)用上面的方法,你就可以獲得當(dāng)前窗口激活的那個(gè)tab的實(shí)例對(duì)象了,從這個(gè)對(duì)象中,你可以獲取到對(duì)應(yīng)的tab唯一的id、url、圖標(biāo)等信息。
值得注意的是,如果chrome瀏覽器打開(kāi)了多個(gè)窗口,就意味著可能每一個(gè)窗口都會(huì)存在一個(gè)激活的tab,因此我們獲取的tab就會(huì)是多個(gè),這個(gè)時(shí)候如果只是解構(gòu)出第一個(gè)可能是不夠嚴(yán)謹(jǐn)?shù)摹?/span>
因此我們可以通過(guò)添加搜索條件來(lái)精確的查找:
async function getCurrentTab() {
let queryOptions={
active: true , currentWindow:true
};
let [tab]=await
chrome.tabs.query(queryOptions);
return tab;
}
通過(guò)添加一個(gè)參數(shù)currentWindow,意味著只搜索腳本運(yùn)行所在窗口的激活的tab,這個(gè)時(shí)候肯定只會(huì)查找出唯一的一個(gè)tab,解構(gòu)第一個(gè)就不會(huì)有問(wèn)題。
搜索條件除了上述之外,還有下面可以選擇:
參數(shù) | 類型 | 作用 |
active | boolean | 是否處于激活狀態(tài) |
audible | boolean | 是否處于播放音頻狀態(tài) |
currentWindow | boolean | 是否處于腳本所在窗口內(nèi) |
groupId | number | 是否處于某個(gè)分組內(nèi) |
highlighted | boolean | 是否處于高亮狀態(tài) |
index | number | 窗口從左往右第index個(gè)tab |
pinned | boolean | 是否處于被固定的狀態(tài) |
status | unloaded/loading/complete | 匹配tab的status為該status的tabs |
title | string | 匹配tab的title為該title的tabs |
url | string | 匹配tab的url為該url的tabs |
windowId | number | 特定窗口下的tabs |
windowType | normal/popup/panel/app/devtools | 特定的窗口類型下所在的tabs |
被固定是指那些通過(guò)右鍵點(diǎn)擊tab的時(shí)候,選擇固定在最左側(cè)的標(biāo)簽,并且可以固定多個(gè)。
該api默認(rèn)支持,需要額外的manifest配置,需要顯式聲明“tabs”的permissions
我們可以很方便的給指定的tab發(fā)送消息,一般來(lái)說(shuō)我們可以在content腳本中做消息的監(jiān)聽(tīng),接收到消息后使其執(zhí)行不同的業(yè)務(wù)邏輯。
chrome.tabs.sendMessage(
tabId: number, // 目標(biāo)tab的id
message: any, // 發(fā)送信息
options?: object, // 其他配置項(xiàng)
callback?: function, // 回調(diào)函數(shù) )
上面這個(gè)是V3版本的插件使用的,在V2版本中我們使用chrome.tabs.sendRquest()
// 在v3版本中已廢棄
chrome.tabs.sendRequest(
tabId: number, // 目標(biāo)tab的id
request: any, // 發(fā)送信息
callback?: function, // 回調(diào)函數(shù)
)
如果我們希望修改一個(gè)tab的一些參數(shù)信息,我們可以選擇使用下面這個(gè)API:
chrome.tabs.update( tabId?: number, updateProperties: object, callback?: function, )
其中updateProperties的值就是上面提到的queryOptions的屬性保持一致,例如我們可以動(dòng)態(tài)的更改指定tab的title、url、pinned等狀態(tài)屬性!
當(dāng)我們按住ctrl的同時(shí)再滑動(dòng)鼠標(biāo)滾輪的話就可以調(diào)整頁(yè)面的縮放比例,這個(gè)可能大家平時(shí)都深有體會(huì),但是實(shí)際上這個(gè)也可以通過(guò)插件給我們提供的API來(lái)動(dòng)態(tài)的進(jìn)行調(diào)整:
chrome.tabs.setZoom( tabId?: number, zoomFactor: number, // 縮放比例 callback?: function, )
我們介紹的第一個(gè)API就展示了如何創(chuàng)建一個(gè)新的tab,他會(huì)默認(rèn)創(chuàng)建在最末尾,也就是最右側(cè),如果這個(gè)放置位置我們不滿意,我們也可以將其放置在我們想要的位置。
移動(dòng)
chrome.tabs.move(
tabIds: number | number[],
moveProperties: object,
callback?: function,
)
type moveProperties={ index?:number, // 想要移動(dòng)至的index索引位置. `-1` 移動(dòng)至窗口末尾.
windowId?:number // 移動(dòng)至的窗口id
}
移除
chrome.tabs.remove( tabIds: number | number[], callback?: function, )
刷新
chrome.tabs.reload( tabId?: number, reloadProperties?: object, callback?: function, ) type reloadProperties={ bypassCache?:boolean // 是否繞過(guò)本地緩存 默認(rèn)不繞過(guò),也就是使用本地緩存。 }
我們可以通過(guò)插件來(lái)控制一個(gè)tab的前進(jìn)后退(如果他們都曾有過(guò)跳轉(zhuǎn)的記錄)
chrome.tabs.goBack( // 回到最近的一次歷史記錄 tabId?: number, callback?: function, ) chrome.tabs.goForward( // 去到下一個(gè)歷史記錄,如果有的話 tabId?: number, callback?: function, )
當(dāng)我們的tab開(kāi)的特別多的時(shí)候,瀏覽器會(huì)有個(gè)小優(yōu)化,對(duì)于某些長(zhǎng)時(shí)間不用的tab,瀏覽器會(huì)清空內(nèi)存中對(duì)其的狀態(tài)存貯,因此當(dāng)我們?cè)俅螌⑵浼せ顣r(shí)會(huì)重新加載!這個(gè)過(guò)程插件也提供了API可以幫助我們做到:
chrome.tabs.discard( tabId?: number, callback?: function, ) chrome.tabs.duplicate( // 這個(gè)API與discard相反,可以幫助我們復(fù)制一個(gè)一摸一樣的tab標(biāo)簽 tabId: number, callback?: function, )
如果我們希望將某些具備相似特征的網(wǎng)站分成一個(gè)組,使其能夠在視圖上更好的被察覺(jué),那么我們就可以通過(guò)插件為我們提供的API來(lái)進(jìn)行實(shí)現(xiàn):
第一步:篩選出希望分到同一組的tabs
const tabs=await chrome.tabs.query({ url: [ "https://developer.chrome.com/*"], });
根據(jù)前面的知識(shí),我們很容易就可以知道tabs就是域名為 "developer.chrome.com" 開(kāi)頭的所有站點(diǎn)的tab集合;
第二步:將他們分為一組
const tabIds=tabs.map(({ id })=> id); const group=await chrome.tabs.group({ tabIds });
上圖中就可以看到所有符合條件的站點(diǎn)就被分為同一個(gè)組了,這個(gè)API的使用方式是:
chrome.tabs.group( options: GroupOptions, callback?: function, ) type GroupOptions={ tabIds?:number[], // 希望被分組的tab的id的集合 groupId?:number, // 已有的分組 createProperties?:{ windowId?:number // 希望分組被創(chuàng)建在那個(gè)窗口, 默認(rèn)是腳本所在窗口 } }
額外的話:
如果我們希望在分組上再加上一個(gè)樣式或者字樣作為標(biāo)記的話,也可以這樣做:
// 第一步: 在manifest.json中添加“tabGrpups”的權(quán)限 { ... "permissions":[ "tabGroups" ] } //第二步: chrome.tabGroups.update(group, { title: "這是分組1" , color:'red' });
就可以修改這個(gè)分組的一些特征,上面是增加了一個(gè)標(biāo)題,效果如下:
以上我們介紹了基本的API,接下來(lái)我們通過(guò)一些案例來(lái)實(shí)際感受一下每個(gè)API的作用:
準(zhǔn)備以下的項(xiàng)目:
manifest.json
{
"name": "tabs demo",
"description": "tabs demo",
"version": "1.0",
"manifest_version": 3,
"action": {
"default_popup": "popup.html",
"default_icon":
{
"16": "/images/get_started16.png",
"32": "/images/get_started32.png",
"48": "/images/get_started48.png",
"128": "/images/get_started128.png"
}
},
"content_scripts": [
{
"js": ["content.js"],
"matches": ["<all_urls>"] } ],
"background":
{
"service_worker": "background.js"
},
"icons": {
"16": "/images/get_started16.png",
"32": "/images/get_started32.png",
"48": "/images/get_started48.png",
"128": "/images/get_started128.png"
},
"permissions": ["tabs", "tabGroups"] }
content.js / background.js
// content.js let color=""; console.log("content.js"); chrome.runtime.onMessage.addListener((message, sender, sendResponse)=> { color=document.body.style.color; document.body.style.background=message; sendResponse("changed"); }); // background.js console.log(chrome);
newtab.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>chrome插件</title> </head> <body> <h1>我是一個(gè)由chrome插件創(chuàng)建的頁(yè)面</h1> </body> </html>
popup.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> </head> <body> <section> <h1>創(chuàng)建新的頁(yè)面</h1> <button id="create-tab">創(chuàng)建</button> </section> <section> <h1>查找符合條件的tab</h1> <div> <span>是否激活</span> <span>是</span> <input type="radio" name="isActive" value="1" /> <span>否</span> <input type="radio" name="isActive" value="0" /> </div> <div> <span>是否屬于當(dāng)前窗口:</span> <span>是</span> <input type="radio" name="isCurrentWindow" value="1" /> <span>否</span> <input type="radio" name="isCurrentWindow" value="0" /> </div> <div> <span>url(支持正則):</span> <input type="text" id="url" /> </div> <div> <span>title</span> <input type="text" id="title" /> </div> <div> <span>index</span> <input type="text" id="index" /> </div> <div> <span>是否被固定</span> <span>是</span> <input type="radio" name="pinned" value="1" /> <span>否</span> <input type="radio" name="pinned" value="0" /> </div> <div> <span>status</span> <select name="status" id="status"> <option value="unloaded">unloaded</option> <option value="loading">unloaded</option> <option value="complete">unloaded</option> </select> </div> <button id="query-tab">查找</button> <div> <div>查找結(jié)果:</div> <div id="search-result"></div> </div> </section> <section> <h1>發(fā)送消息</h1> <input type="color" id="send-value" /> <button id="send-btn">變色吧</button> </section> <section> <h1>刪/改/移/丟棄/復(fù)制</h1> <div> <input type="text" id="move-index" /> <button id="move">移動(dòng)當(dāng)前的tab</button> </div> <div> <button id="remove">移除當(dāng)前的tab</button> </div> <div> <button id="reload">刷新當(dāng)前的tab</button> </div> <div> <input type="text" id="discard-value" /> <button id="discard">丟棄</button> </div> <div> <button id="duplicate">復(fù)制</button> </div> <div> <input type="text" id="update-value" /> <button id="update">更新</button> </div> </section> <section> <h1>縮放比</h1> <div> <input type="text" id="zoom" /> <button id="zoom-btn">調(diào)整</button> </div> </section> <section> <h1>分組</h1> <div> <input type="text" id="group-title" /> <button id="group">使用查詢的結(jié)果進(jìn)行分組</button> </div> </section> <section> <h1>導(dǎo)航</h1> <div> <button id="goForward">前進(jìn)</button> <button id="goBack">前進(jìn)</button> </div> </section> <script src="./popup.js"></script> </body> </html>
popup.js
document.getElementById("create-tab").addEventListener("click", ()=> { chrome.tabs.create({ url: "newtab.html", // 相對(duì)于background腳本的路徑下需要有一個(gè)newtab.html文件 }); }); let Tabs=[]; const getSelect=(list)=> { const yes=list[0]; const no=list[1]; if (yes.checked) { return yes.defaultValue==="1"; } if (no.checked) { return no.defaultValue==="1"; } return false; }; document.getElementById("query-tab").addEventListener("click", async ()=> { const active=getSelect(document.getElementsByName("isActive")); const currentWindow=getSelect( document.getElementsByName("isCurrentWindow") ); const pinned=getSelect(document.getElementsByName("pinned")); const url=document.getElementById("url").value; const title=document.getElementById("title").value; const index=document.getElementById("index").value; const queryOptions={ active, currentWindow, pinned, }; if (url) { queryOptions.url=url; } if (title) { queryOptions.title=title; } if (index) { queryOptions.index=index - 0; } console.log(queryOptions); const tabs=await chrome.tabs.query(queryOptions); document.getElementById("search-result").innerHTML=JSON.stringify( tabs.map(({ id })=> ({ id })) ); Tabs=tabs; }); document.getElementById("send-btn").addEventListener("click", async ()=> { const color=document.getElementById("send-value").value; const [tab]=await chrome.tabs.query({ active: true, currentWindow: true }); const response=await chrome.tabs.sendMessage(tab.id, color); console.log(color, response); }); const getCurrentTab=async ()=> { const [tab]=await chrome.tabs.query({ active: true, currentWindow: true }); return tab.id; }; document.getElementById("move").addEventListener("click", async ()=> { const index=document.getElementById("move-index").value - 0; const tabIds=await getCurrentTab(); chrome.tabs.move(tabIds, { index }); }); document.getElementById("remove").addEventListener("click", async ()=> { const tabIds=await getCurrentTab(); chrome.tabs.remove(tabIds); }); document.getElementById("reload").addEventListener("click", async ()=> { const tabId=await getCurrentTab(); chrome.tabs.reload(tabId); }); document.getElementById("discard").addEventListener("click", async ()=> { const tabId=document.getElementById("discard-value").value - 0; chrome.tabs.discard(tabId); }); document.getElementById("duplicate").addEventListener("click", async ()=> { const tabId=await getCurrentTab(); chrome.tabs.duplicate(tabId); }); document.getElementById("zoom-btn").addEventListener("click", async ()=> { const tabId=await getCurrentTab(); const zoomFactor=document.getElementById("zoom").value - 0; chrome.tabs.setZoom(tabId, zoomFactor); }); document.getElementById("group").addEventListener("click", async ()=> { const tabIds=Tabs.map(({ id })=> id); const title=document.getElementById("group-title").value; const group=await chrome.tabs.group({ tabIds }); chrome.tabGroups.update(group, { color: "red", title }); }); document.getElementById("goForward").addEventListener("click", async ()=> { const tabId=await getCurrentTab(); chrome.tabs.goForward(tabId); }); document.getElementById("goBack").addEventListener("click", async ()=> { const tabId=await getCurrentTab(); chrome.tabs.goBack(tabId); });
以上的資源我會(huì)放到github上,大家可以download下來(lái)直接在自己的瀏覽器上運(yùn)行,查看效果,也希望有收獲后給不吝star哈!。
下面是我本地的測(cè)試效果:
創(chuàng)建頁(yè)面/發(fā)送消息
查詢
刪/改/更新
分組
有了以上的武器,就可以玩轉(zhuǎn)tabs啦!一起開(kāi)始開(kāi)發(fā)chrome插件吧!
以下是我的其他文章,歡迎閱讀
保姆級(jí)講解JS精度丟失問(wèn)題(圖文結(jié)合)
shell、bash、zsh、powershell、gitbash、cmd這些到底都是啥?
從0到1開(kāi)發(fā)一個(gè)瀏覽器插件(通俗易懂)
用零碎時(shí)間個(gè)人建站(200+贊)
另外我有一個(gè)自己的網(wǎng)站,歡迎來(lái)看看 new-story.cn
創(chuàng)作不易,如果您覺(jué)得文章有任何幫助到您的地方,或者觸碰到了自己的知識(shí)盲區(qū),請(qǐng)幫我點(diǎn)贊收藏一下,或者關(guān)注我,我會(huì)產(chǎn)出更多高質(zhì)量文章,最后感謝您的閱讀,祝愿大家越來(lái)越好。
*請(qǐng)認(rèn)真填寫(xiě)需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。