A lot of people talk about Chrome plugins, but I have to say that plug-in development is about playing with a small toy. The first factor is utility, the second is fun. It’s boring to just list all the features. So here is an old article to share with you.

People, live is to lai skin.

As a qualified developer, it is recommended that you spend 30% of your time slacking.

Because if you can’t beat the clock, it doesn’t mean you’re working hard, it means you’re not automating enough.

They usually spend their time at work browsing: SGamer forums, Hupu Forums, Douyu, BiliBili, etc.

However, the following pain points will be encountered during the browsing process:

  • Boss check, post or live broadcast room to open too much, can not close all of the site in a timely manner.
  • The boss went to re-lai skin, do not remember before the opening of the post or broadcast where.
  • Every time in the browser to enter a bad url, typing really trouble!
  • Having too many tabs open at work makes it hard to find the pages you want when you rest.

So, we need to:

Simple one-click renegade plug-in features:

  1. Open the browser, a shortcut key, immediately open the page, happy to start the day.
  2. When the boss /leader checks up, a shortcut button instantly closes all rogue pages.
  3. After the boss leaves, or after working for a period of time, a shortcut key immediately opens the original rascally post and live broadcast room.

Simple one-click renegade plug-in features:

  1. Contains simple one-click deadbeat site functionality
  2. Can customize the configuration of laipi site.
  3. Upload Google and publish the plugin.

Develop a simple one-click plug-in from scratch

Chrome is used by 90% of office workers, so we chose to use Chrome plugin to achieve the function.

The Chrome plugin is not a big deal, it still uses the combination of HTML\CSS\JS.

Here, I’ll walk you through making plug-ins from scratch.

mainfest.json

Just like package.json for Node.js, each plug-in must have a manifest.json file as the initial configuration file.

Let’s create a new project, create manifest.json in the root directory, and fill in the following code

==mainfest.json==

 {
  "name": "One-click turn-off tool for work."."version": "0.1"."description": "Windows: Press Alt+S to open and close Laipi website \ NMac: Press Control+S to open and close Laipi website"."manifest_version"2} :Copy the code

Explain:

  • Name: the name of the plug-in
  • Version: indicates the version of the plug-in
  • Description: Plug-in introduction bar
  • Manifest_version: This is written out. Every file must have it

Next, right-click and save the logo in the root directory as apple-touch-icon.png.

You can also go to p1-jj.byteimg.com/tos-cn-i-t2… Save the picture

Modify mainfest.json to make all four sizes of ICONS become apple-touch-icon. PNG and the plugin bar also display Apple-touch-icon.png.

==mainfest.json==

{
  "name": "One-click turn-off tool for work."."version": "0.1"."description": "Windows: Press Alt+S to open and close Laipi website \ NMac: Press Control+S to open and close Laipi website"."icons": {
    "16": "apple-touch-icon.png"."32": "apple-touch-icon.png"."48": "apple-touch-icon.png"."128": "apple-touch-icon.png"
  },
  "browser_action": {
    "default_icon": "apple-touch-icon.png"."default_popup": "popup.html"
  },
  "commands": {
    "toggle-tags": {
      "suggested_key": {
        "default": "Alt+S"."mac": "MacCtrl+S"
      },
      "description": "Toggle Tags"}},"manifest_version"2} :Copy the code

Explain:

  • icons: Configures ICONS to display in different places
  • browser_action: plug-in in the upper right corner,browser_action > default_iconThat is, the plug-in icon in the upper right corner
  • commands: Used for shortcut key commands.commands > toggle-tags > suggested_keyUnder that, you set up a shortcut key that will publish one to Chrome in the background when pressedcommandAnd has a value oftoggle-tags.

On Windows, we set the shortcut to Alt+S, and on MAC, we set the shortcut to Control+S. The configuration file reads MacCtrl+S

Now that we have the command, we need the background script to receive it. Add the background script to mainfest.json: ==mainfest.json==

."background": {
    "scripts": [
      "background.js"]}...Copy the code

Create background.js. ==background.js== in the root directory

chrome.commands.onCommand.addListener(function(command) {
    alert(command)
    console.log(command)})Copy the code

Now our directory structure is as follows:

├─ manifest.json ├─ background.js ├─ sgans.pngCopy the code

Load the plug-in in Chrome

Click the three dot button in the upper right corner of Chorme… > More Tools > Extensions

Open Developer Mode in the upper right corner

Find LOAD UNPACKED at the top and import the root directory of the project

After the project is imported, a new card will appear with this effect:

At this point, if you press Alt+S in Windows, a message will pop up, which is toggle-Tags, as we defined in Mainfest.json.

At the same time, we can click on the background page indicated by the blue key in the above image to open a debugging tool and see the output of toggle-Tags.

After we edit the plug-in locally, we can press the refresh button indicated by the gray key header, and the new function can be refreshed and loaded immediately!

Having these jobs means you can take the next step!

TAB page configuration

One key to open/close the site, the implementation principle is actually chrome TAB page function.

TAB page functionality access requires adding permissions in manifest.json ==mainfest.json==

."permissions": ["tabs"]...Copy the code

Next, we write that background-js creates a new home page using the shortcut (Alt+S for Windows, or Ctrl+S for MAC) : == background-js ==

// Enter the home page of the site you want const MainPageUrl ='http://https://bbs.hupu.com/all-gambia'

chrome.commands.onCommand.addListener(function (command) {
  if (command= = ='toggle-tags') {
    chrome.tabs.create({"url": MainPageUrl, "selected": true}); }})Copy the code

In fact, the implementation is very simple, just call chrome. Tabs. Create interface, create a new TAB page. Refresh the plugin and try the shortcut function ———— to see if you can already control the pop-up TAB page in the browser!

Implement concrete logic:

A bit more complicated is the handling of the isOpen state of the TAB page. The following diagram focuses on changes in the isOpen state and tabCache values.

The specific background logic is as follows, which can be understood by referring to the remarks and flow chart:

// Initialize the isOpen and tabCache stateslet isOpen = false
letTabCache = [] // New TAB open home page const mainPageUrl ='https://bbs.hupu.com/all-gambia'Const myPattern = const myPattern ='sgamer\.com/|douyu\.com|hupu\.com|bilibili\.com'// The Url of the current pagelet currentPageUrl = ' '/** * Start step: Check isOpen status * Case 1: isOpen istrue* Case 2: isOpen isfalse, reloading the page * mands. / chrome.com onCommand. AddListener (function (command) {
  if (command= = ='toggle-tags') {
    if(isOpen) {// Case 1: isOpen istrueRemovePages (myPattern) // Case 2: isOpenfalse
    } else{reloadPages(myPattern, mainPageUrl)}}}) Remove page * 1, clear tabCache * 2, close all domain tags * 3, store closed tags in tabCache array * 4, change isOpen status to isOpenfalse* /function removePages(patternStr) {
  tabCache = []
  chrome.tabs.query({active: true}, function (tab) {
    currentPageUrl = tab[0].url
  })
  let pattern = new RegExp(patternStr)
  walkEveryTab(function (tab) {
    if (pattern.test(tab.url)) {
      chrome.tabs.remove(tab.id,function(){
        tabCache.push(tab.url)
      })
    }
  },function(){
    isOpen = false})} /** * Case 2: reload the page * Check whether there is cache: * Case 2-1 No cache: open a new TAB or locate a TAB in the domain name * Case 2-2 With cache: Open all cached pages */function reloadPages(patternStr, mainPageUrl) {
  if (tabCache.length === 0) {
    focusOrCreateTab(patternStr, mainPageUrl)
  } elseOpenAllCachedTab (tabCache)}} /** * Open a new label or locate a label * in the domain name. 1. Traverse all labels and record the URL of the label matching the domain name and the last label *true* 1, obtain the current page URL * 2, if the current page URL does not match the domain name, locate the TAB, change the isOpen state totrue* 3. If the current page URL matches the domain name, close all tabs (as in case 1) and change the isOpen state to isOpenfalse* /function focusOrCreateTab(patternStr, url) {
  let pattern = new RegExp(patternStr)
  let theTabs = []
  let theLastTab = null
  walkEveryTab(function (tab) {
      if (pattern.test(tab.url)) {
        theTabs.push(tab.url)
        theLastTab = tab
      }
    }, function () {
      if (theTabs.length > 0) {
        chrome.tabs.query({active: true}, function (tab) {
          let currentUrl = tab[0].url
          if (theTabs.indexOf(currentUrl) > -1) {
            removePages(patternStr)
            isOpen = false
          } else {
            chrome.tabs.update(theLastTab.id, {"selected": true});
            isOpen = true}})}else {
        chrome.tabs.create({"url": url, "selected": true});
        isOpen = true} /** * open all tabCache tabs again ** * change the isOpen statetrue* /function openAllCachedTab(tabCache) {
  let focusTab = null
  tabCache.forEach(function (url, index) {
    chrome.tabs.create({'url': url}, function (tab) {
      if (tab.url === currentPageUrl) {
        focusTab = tab.id
      }
      if (index === tabCache.length-1 - 1) {
        if (focusTab) {
          chrome.tabs.update(focusTab, {"selected": true},function(){
          });
        }
      }
    })
  })
  isOpen = true} /** ** @param callback * @param lastCallback * wrap the function that iterates through all labels, creating two callbacks. * A callback is executed during each iteration. * A callback is executed once the entire loop has been traversed. * /function walkEveryTab(callback, lastCallback) {
  chrome.windows.getAll({"populate": true}, function (windows) {
    for (let i in windows) {
      let tabs = windows[i].tabs;
      for (let j in tabs) {
        let tab = tabs[j];
        callback(tab)
      }
    }
    if(lastCallback) lastCallback()
  })
}

Copy the code

Upload and publish plug-ins

We need to publish the plugin in Chrome’s Developer center and access the Developer Dashboard

Well, a simple to use to work rascality plug-in is ready! In debug mode, you can use CTRL + S to quickly find, open, close, and reopen dead pages. Anytime and anywhere, all-round lai PI, calmly face the boss check.

Configurable advanced rascal plug-in

Now I want my plugin to be ready to configure the site:

Then you need to use Chrome.storage.

You need to enable storage permission:

The manifest. Add in json

."permissions": [
    "tabs"."storage"],...Copy the code

Then use the

chrome.storage.local.set({
        'value1':theValue1,
        'value2',theValue2
})
Copy the code

This form is used to store storage. After the use

chrome.storage.local.get(['value1'],(res)=>{
    const theValue1 = res.value1
})
Copy the code

So that gives you the value of the store.

Started to rewritebackground.js

We changed mainPageUrl and myPattern to fetch from storage.

const INIT_SITES_LIST = ['bilibili.com'.'douyu.com'.'sgamer.com'.'hupu.com']
const INIT_MAIN_PAGE = 'https://bbs.hupu.com/all-gambia'/ / set up good storage in the install chrome. Runtime. OnInstalled. AddListener (function() { chrome.storage.local.set({ sites: INIT_SITES_LIST, mainPage:INIT_MAIN_PAGE }) }); // Initialize the isOpen and tabCache stateslet isOpen = false
let tabCache = []
let currentPageUrl = ' '/** * Start step: Check isOpen status * Case 1: isOpen istrue* Case 2: isOpen isfalse, reloading the page * mands. / chrome.com onCommand. AddListener (function (command) {
  if (command= = ='toggle-tags') {
    chrome.storage.local.get(['sites'.'mainPage'].function(res){
      let sites =  res.sites
      let mainPageUrl = res.mainPage
      let myPattern = sites.map(item=>item.replace('. '.'\ \.)).join('|')
      console.log(myPattern)

      if(isOpen) {// Case 1: isOpen istrueRemovePages (myPattern) // Case 2: isOpenfalse
      } else{reloadPages (myPattern mainPageUrl)}})}}) / / = = = = = = = = = = = = = = = = = = = = = = = = don't need to change the following part, see here is enough) / * * * case 1: Remove page * 1, clear tabCache * 2, close all domain tags * 3, store closed tags in tabCache array * 4, change isOpen status to isOpenfalse* /function removePages(patternStr) {
  tabCache = []
  chrome.tabs.query({active: true}, function (tab) {
    currentPageUrl = tab[0].url
  })
  let pattern = new RegExp(patternStr)
  walkEveryTab(function (tab) {
    if (pattern.test(tab.url)) {
      chrome.tabs.remove(tab.id,function(){
        tabCache.push(tab.url)
      })
    }
  },function(){
    isOpen = false})} /** * Case 2: reload the page * Check whether there is cache: * Case 2-1 No cache: open a new TAB or locate a TAB in the domain name * Case 2-2 With cache: Open all cached pages */function reloadPages(patternStr, mainPageUrl) {
  if (tabCache.length === 0) {
    focusOrCreateTab(patternStr, mainPageUrl)
  } elseOpenAllCachedTab (tabCache)}} /** * Open a new label or locate a label * in the domain name. 1. Traverse all labels and record the URL of the label matching the domain name and the last label *true* 1, obtain the current page URL * 2, if the current page URL does not match the domain name, locate the TAB, change the isOpen state totrue* 3. If the current page URL matches the domain name, close all tabs (as in case 1) and change the isOpen state to isOpenfalse* /function focusOrCreateTab(patternStr, url) {
  let pattern = new RegExp(patternStr)
  let theTabs = []
  let theLastTab = null
  walkEveryTab(function (tab) {
      if (pattern.test(tab.url)) {
        theTabs.push(tab.url)
        theLastTab = tab
      }
    }, function () {
      if (theTabs.length > 0) {
        chrome.tabs.query({active: true}, function (tab) {
          let currentUrl = tab[0].url
          if (theTabs.indexOf(currentUrl) > -1) {
            removePages(patternStr)
            isOpen = false
          } else {
            chrome.tabs.update(theLastTab.id, {"selected": true});
            isOpen = true}})}else {
        chrome.tabs.create({"url": url, "selected": true});
        isOpen = true} /** * open all tabCache tabs again ** * change the isOpen statetrue* /function openAllCachedTab(tabCache) {
  let focusTab = null
  tabCache.forEach(function (url, index) {
    chrome.tabs.create({'url': url}, function (tab) {
      if (tab.url === currentPageUrl) {
        focusTab = tab.id
      }
      if (index === tabCache.length-1 - 1) {
        if (focusTab) {
          chrome.tabs.update(focusTab, {"selected": true},function(){
          });
        }
      }
    })
  })
  isOpen = true} /** ** @param callback * @param lastCallback * wrap the function that iterates through all labels, creating two callbacks. * A callback is executed during each iteration. * A callback is executed once the entire loop has been traversed. * /function walkEveryTab(callback, lastCallback) {
  chrome.windows.getAll({"populate": true}, function (windows) {
    for (let i in windows) {
      let tabs = windows[i].tabs;
      for (let j in tabs) {
        let tab = tabs[j];
        callback(tab)
      }
    }
    if(lastCallback) lastCallback()
  })
}



Copy the code

So we can write a popup page that will display if we click on the icon, as shown here:

Now let’s complete the popup.html and popup.css and pupup.js pages

All js files can be called directly to Chrome.storage.local.get

Just pay special attention to the chrome.storage call part of the js file

We’re not here to learn page layout

popup.html

< HTML > <head> <title> </title> <link rel="stylesheet" href="popup.css">
</head>
<body>
<div class="container">
  <h2 class="lapi-title"> </h2> <ul class="lapi-content">
  </ul>
  <p>
  <label><input type="text" id="add" class="add"></label>
  <button class="button add-button "> + < / button > < / p > < p > < / p > < p > < / p > < p > < / p > < h2 > my heels home page < / h2 > < div id ="change-content">
  <span class="main-page-inactive" id="main-page-inactive"></span><button class="button change-button " id="change"> ✎ < / button > < / div > < p class ="lapi-tip"< span class = > press"lapi-key">Alt+S</span> Quickly open/close the site </p> </div> <script SRC ="zepto.min.js"></script>
<script src="popup.js"></script>
</body>
</html>
Copy the code

popup.css

* {
    margin: 0;
    padding: 0;
    color:#6a6f77;
}


input, button, select, textarea {
    outline: none;
    -webkit-appearance: none;
    border-radius: 0;
    border: none;
}

input:focus{
    list-style: none;
    box-shadow: none;
}

ol, ul {
    list-style: none;
}

li{
    margin: 5px 0;
}

.container {
    width: 200px;
    padding: 10px;
}

.container h2{
    margin: 10px;
    text-align: center;
    display: block;
}

.lapi-content li{
    transition: opacity 1s;
}

.site{
    cursor: pointer;
    color: #00b0ff;} .add, .main-page{ box-sizing: border-box; text-align:center; font-size:14px; /*height:27px; */ border-radius:3px; border:1px solid#c8cccf;
    color:#6a6f77;
    outline:0;
    padding:0 10px;
    text-decoration:none;
    width: 170px;
}

#main-page{
    font-size: 12px;
    text-align: left;
    width: 166px;
    margin: 0;
    padding: 2px;
}

.add{
    height: 27px;
}

.main-page{
    width: 170px;
    outline: none;
    resize: none;

}

.main-page-inactive{
    width: 160px;
    line-break: auto;
    word-break: break-word;
    overflow: hidden;
    display: inline-block;
    cursor: pointer;
    color: #00b0ff;
    margin: 3px;
}

.button{

    font-size: 16px;
    /*border: 1px solid #c8cccf; * /
    color: #c8cccf;/*border: none; */ padding: 0 4px 1px 3px; border-radius: 3px; } .close-button{ transition: all 1s; } .button:hover{ background:#E27575;
    color: #FFF;
}

.add-button{
    transition:all 1s;
    font-size: 20px;
    padding: 0 6px 1px 5px;
}

.change-button{
    position: absolute;
    transition:all 1s;
    font-size: 20px;
    padding: 0 6px 1px 5px;
}
.change-button:hover{
    background: #f9a825;
    color: #FFF;
}

#change-check{
    color: #f9a825;
}

#change-check:hover{
    color: #fff;
}

.add-button:hover{
    background: #B8DDFF;
    color: #FFF;
}

.submit{
    transition: all 1s;
    margin: 10px;
    padding: 5px 10px;
    font-size: 16px;
    border-radius: 4px;
    background: #B8DDFF;
    border: 1px solid #B8DDFF;
    color: #FFF;
}

.submit:hover{
    border: 1px solid #B8DDFF;
    background: #fff;
    color: #B8DDFF;
}

.fade{
    opacity: 0;
}

.add-wrong,.add-wrong:focus{
    border: #e91e63 1px solid;Box - shadow: 0 0 5 px rgba (233,30,99, 3); } .lapi-tip{ margin-top: 20px; border-top: 1px solid#c8cccf;
    padding-top: 8px;
    color: #c8cccf;
    text-align: center;
}

.lapi-key{
    color: #B8DDFF;

}
Copy the code

Focus on:chrome.storagePart of the

popup.js

let sites = []
let mainPage = ' '
const isMac = /Macintosh/.test(navigator.userAgent)
let $lapiKey = $('.lapi-key')

isMac? $lapiKey.text('Control+S') :$lapiKey.text('Alt+S'// Select the site and mainPage fields from the storage and set them on the page. chrome.storage.local.get(['sites'.'mainPage'].function (res) {
  if (res.sites) {
    sites = res.sites
    mainPage = res.mainPage
    sites.forEach(function (item) {
      let appendEl = '<li><span class="site">' + item + '</span>\n' +
        '<button class="button close-button">&times</button>\n' +
        '</li>'
      $('ul.lapi-content').append(appendEl)
    })

  }
  $('#main-page').val(mainPage)
  $('#main-page-inactive').html(mainPage)
})


$('#save').on('click'.function () {
  alert()
})

$('#change-content').delegate('#main-page-inactive'.'click'.function() {let mainPageUrl = $(this).html()
  if(/^http:\/\/|^https:\/\//.test(mainPageUrl)){
    chrome.tabs.create({"url": mainPageUrl, "selected": true})}else{
    chrome.tabs.create({"url": 'http://'+mainPageUrl, "selected": true})}})let addEl = $('#add')
addEl.focus()
let lapiCon = $('ul.lapi-content')

lapiCon.delegate('.close-button'.'click'.function () {
  let $this = $(this)
  let siteValue = $this.siblings().html()
  sites = sites.filter(function (item) {
    returnitem ! == siteValue }) chrome.storage.local.set({sites: sites})$this.parent().addClass('fade')
  setTimeout(function () {
    $this.parent().remove()
  }, 800)
})


$('.add-button').on('click',addEvent)
addEl.bind('keypress'.function(event){
  if(event.keyCode === 13) addEvent()
})


function addEvent() {if(! validate(addEl.val())){ addEl.addClass('add-wrong')}else{
    let appendEl = '<li><span class="site">' + addEl.val() + '</span>\n' +
      '<button class="button close-button">&times</button>\n' +
      '</li>'
    $('ul.lapi-content').append(appendEl)
    sites.push(addEl.val())
    chrome.storage.local.set({sites:sites})
    addEl.removeClass('add-wrong')
    addEl.focus().val(' ')}}function validate(value){
  value = value.trim()
  if(value.length ===0){
    return false
  }
  return/^([\w_-]+\.) *[\w_-]+$/.test(value) } lapiCon.delegate('.site'.'click'.function() {let siteUrl = $(this).html()
  chrome.tabs.create({"url": 'http://'+siteUrl, "selected": true$(})})'#change-content').delegate('#change'.'click'.function(){
  changeMainPage($(this))
}).delegate('#change-check'.'click'.function(){
  changeCheck($('#change-check'))
}).delegate('#main-page'.'blur'.function(){
  changeCheck($('#change-check'))})function changeMainPage($this) {$this.siblings().remove()
  $this.parent().prepend('<label><textarea id="main-page" class="main-page"></textarea></label>')
  $this.parent().append(< grid class=" grid change- grid "id=" grid change-check">✓
      'The $()'#main-page').val(mainPage).focus()
  $this.remove()
}


function changeCheck($this) {let mainPageVal = $('#main-page').val()
  $this.siblings().remove()
  $this.parent().prepend('<span class="main-page-inactive" id="main-page-inactive"></span>'The $()'#main-page-inactive').text(mainPageVal)
  chrome.storage.local.set({mainPage:mainPageVal})
  $this.parent().append('< button class = "button change - button" id = "change" > ✎ < / button >')}Copy the code

Ok, an elegant plugin is ready, you can check out github.com/wanthering/…

Have fun at work. Stop hiding in the toilet and playing with your cell phone. It’s not healthy! Rascal plug-in to use!