前端

Chrome extension cookies

permissions

set the cookies permission and the domain you would like to access cookies.

"permissions": {
    "cookies",
    "*://*.example.com/"
}

type

cookie

just a simple object with {name, value, domain...}

CookieStore

normal mode and incognito mode use different cookie stores.

read

get: chrome.cookies.get({url: URL, name: COOKIE_NAME, storeId: COOKIE_STORE_ID}, function(cookie) {})

get all: chrome.cookies.get({domain: DOMAIN}, function(cookies) {}) NOTE: there are other filters not listed here.

set: chrome.cookies.set({url, name, value}, function(cookie) {}) if failed, the callback gets null

thinking in vue

basic usage

The created vue instance will proxy its data member

<div id="app">
  <ol>
    <li v-for="company in companies">
      <a v-bind:href="company.link">{{ company.text }}</a>  // bind 更新参数
            <button v-on:click="reverseText">逆转消息</button>
    </li>
  </ol>
</div>

let vm = new Vue({
    el: '#app',
    data: {
        companies: [
                {text: 'Google', link: 'http://google.com'},
                    {text: 'fackbook', link: 'http://facebook.com'},
                    {text: 'apple', link: 'http://apple.com'}
            ]
    },
    methods: {
        reverseText: function() {
        // 注意 this 绑定到了触发这个事件的元素内部
                this.company.text = this.company.text.split('').reverse.join('');
            }
    }
});

vm.companies.push({text: 'Amazon', link: 'http://amazon.com'});
// computed 属性可以绑定一个虚拟的属性到几个不同的属性上,有点类似 python 的 @property
// ...
computed: {
  fullName: {
    // getter
    get: function () {
      return this.firstName + ' ' + this.lastName
    },
    // setter
    set: function (newValue) {
      var names = newValue.split(' ')
      this.firstName = names[0]
      this.lastName = names[names.length - 1]
    }
  }
}
// ...
# 适合用来做自动保存文档等工作
<script>
var watchExampleVM = new Vue({
  el: '#watch-example',
  data: {
    question: '',
    answer: 'I cannot give you an answer until you ask a question!'
  },
  watch: {
    // 如果 question 发生改变,这个函数就会运行
    question: function (newQuestion) {
      this.answer = 'Waiting for you to stop typing...'
      this.getAnswer()
    }
  },
  methods: {
    // _.debounce 是一个通过 lodash 限制操作频率的函数。
    // 在这个例子中,我们希望限制访问 yesno.wtf/api 的频率
    // ajax 请求直到用户输入完毕才会发出
    // 学习更多关于 _.debounce function (and its cousin
    // _.throttle),参考:https://lodash.com/docs#debounce
    getAnswer: _.debounce(
      function () {
        if (this.question.indexOf('?') === -1) {
          this.answer = 'Questions usually contain a question mark. ;-)'
          return
        }
        this.answer = 'Thinking...'
        var vm = this
        axios.get('https://yesno.wtf/api')
          .then(function (response) {
            vm.answer = _.capitalize(response.data.answer)
          })
          .catch(function (error) {
            vm.answer = 'Error! Could not reach the API. ' + error
          })
      },
      // 这是我们为用户停止输入等待的毫秒数
      500
    )
  }
})
</script>

flow control

v-if

v-else 元素必须紧跟在 v-if 或者 v-else-if 元素的后面——否则它将不会被识别。v-else-if 也是。

可以使用 template 来包装多个元素:

<template v-if="ok">
  <h1>Title</h1>
  <p>Paragraph 1</p>
  <p>Paragraph 2</p>
</template>

v-for

v-for 的基本语法如前所述,另外还可以采用可选参数 key. <li v-for="(item, index) in items">.

v-for 还可以遍历对象,<li v-for="value in obj">

v-for 还可以直接遍历 range, <li v-for="n in 10">

事件

在 vue 中,绑定的事件如果需要参数,可以使用

<button v-on:click="warn('Form cannot be submitted yet.', $event)">
  Submit
</button>

的形式,其中 $event 指的是原声事件。

修饰符

vue 中的事件绑定函数可以使用一些修饰符来指定一些附加的效果。常用的有 .prevent, .stop, .self, .once 等。

像这样:<a v-on:click.stop="doThis"></a>

对于键盘时间,还可以使用修饰符来指定键值:<input v-on:keyup.enter="submit">

mustache vs v-bind

mustache can only be used in textContent of an element, v-bind is used for attribute.

<a v-bind:href="url">{{ link_text }}</a>

shortcut

<!-- 完整语法 -->
<a v-bind:href="url"></a>
<!-- 缩写 -->
<a :href="url"></a>
<!-- 完整语法 -->
<a v-on:click="doSomething"></a>
<!-- 缩写 -->
<a @click="doSomething"></a>

v-model 做双向绑定

use v-model to double bind data between input and js.

<div id="app-6">
  <p>{{ message }}</p>
  <input v-model="message">
</div>

var app6 = new Vue({
  el: '#app-6',
  data: {
    message: 'Hello Vue!'
  }
})

值得注意的是,v-model 本质上只是一个语法糖。
<input v-model="something"> is just a syntax sugar to <input v-bind:value="something" v-on:input="something = $event.target.value">

text area

需要注意的是,textarea 时间上相当于一个 input 组件,不能在 testarea 内部使用 {{value}} 的语法,而应该使用 v-model

<span>Multiline message is:</span>
<p style="white-space: pre-line;">{{ message }}</p>
<br>
<textarea v-model="message" placeholder="add multiple lines"></textarea>

修饰符

就像事件一样,也可以指定一些修饰符给 v-model, 常用的有 .trim

vue-component

vue 的 component 中三个重要的概念:props, events, slots.

其中 props 向下传递,用于 parent 组件向 child 组件传递值。child 组件对于 props 的访问只能是只读的。在 child 组件中使用v-bind:var="var"来访问定义的 props. 注意在组件中不能更改 props, 如果需要更改他,请把他赋值给其他变量,或者使用 computed 属性。

如果把模板直接放到 dom 中会有一些标签渲染不出来,建议放到

// 在 vue 中注册一个组件,大多数传递给 vue 实例的参数都可以使用,除了 data 必须是一个函数

Vue.component('todo-item', {
  // todo-item 组件现在接受一个"prop",类似于一个自定义属性。这个属性名为 todo。
  props: ['todo'],
  template: '<li>{{ todo.text }}</li>',
  data: function() {},
})

<div id="app-7">
  <ol>
    <!-- 现在我们为每个 todo-item 提供待办项对象    -->
    <!-- 待办项对象是变量,即其内容可以是动态的 -->
    <todo-item v-for="item in groceryList" v-bind:todo="item"></todo-item>
  </ol>
</div>

Vue.component('todo-item', {
  props: ['todo'],
  template: '<li>{{ todo.text }}</li>'
})
var app7 = new Vue({
  el: '#app-7',
  data: {
    groceryList: [
      { text: '蔬菜' },
      { text: '奶酪' },
      { text: '随便其他什么人吃的东西' }
    ]
  }
})

事件

parent 组件可以监听子组件的事件,从而实现通信:

这个例子中,子组件通过 $emit() 函数发送 increment 事件。parent 组件通过监听子组件的 increment 事件,从而获得子组件的消息。

<div id="counter-event-example">
  <p>{{ total }}</p>
  <button-counter v-on:increment="incrementTotal"></button-counter>
  <button-counter v-on:increment="incrementTotal"></button-counter>
</div>
Vue.component('button-counter', {
  template: '<button v-on:click="incrementCounter">{{ counter }}</button>',
  data: function () {
    return {
      counter: 0
    }
  },
  methods: {
    incrementCounter: function () {
      this.counter += 1
      this.$emit('increment')
    }
  },
})
new Vue({
  el: '#counter-event-example',
  data: {
    total: 0
  },
  methods: {
    incrementTotal: function () {
      this.total += 1
    }
  }
})

slots

slots vs props: slots 用于显示一大片的包含 html 代码的替换块,而 props 用于显示值,有点类似 v-bind 和 {{}} 的区别。

Props 允许外部环境传递数据给组件
Events 允许从外部环境在组件内触发副作用
Slots 允许外部环境将额外的内容组合在组件中。

vue router

basic: 将组件 (components) 映射到路由 (routes),然后告诉 vue-router 在哪里渲染它们。

<div id="app">
  <p>
    <router-link to="/user/foo">/user/foo</router-link>
    <router-link to="/user/bar">/user/bar</router-link>
  </p>
  <router-view></router-view>
</div>

<script>
const User = {
  template: `<div>User {{ $route.params.id }}</div>`
}

const router = new VueRouter({
  mode: 'history',  // 这样才能使用 html5 的 history api
  routes: [
    { path: '/user/:id', component: User }
  ]
})

const app = new Vue({ router }).$mount('#app')
</script>

vue lifecycle

using in chrome extension

chrome does not allow eval and new Function() in extensions, vue relies on it. you need to use CSP version of vue or relax the restriction by chrome.

See also:

  • https://developer.chrome.com/extensions/contentSecurityPolicy#relaxing-eval
  • https://stackoverflow.com/questions/34615503/vue-js-in-chrome-extension
  • https://vuejs.org/v2/guide/installation.html#CSP-environments

Chrome 扩展插件开发

A chrome extension can inject script into the page, this is called content script.

https://developer.chrome.com/extensions/getstarted
https://developer.chrome.com/extensions/content_scripts
https://developer.chrome.com/extensions/messaging

图标变灰的问题

Add browseraction.defaulticon in your manifest.json file

{
  ...

  "browser_action": {
    "default_icon": "icons/icon-32.png";
  },

  ...
}

JavaScript 网页选择 API

basics

window.getSelection and document.getSelection all returns the Selection object, the selection object is almost useless.

window.getSelection.getRangeAt(0) returns a Range object. for history reasons, there is only one range in each selection.

rangeAncestor = range.commonAncestorContainer; commonAncestorContainer is the common ancestor of the range elements.

使用 React-Bootstrap

BootStrap 适合做什么

Bootstrap 适合做没有界面设计,只有功能或者草图,直接通过代码快速的生成一个可用的界面

Bootstrap 不适合:

  1. 已经有了良好的界面设计,包含了各种细节
  2. 在已有的非 Bootstrap 的界面上进行改动的时候

居中

  • 文本居中可以使用 .text-center 属性.
  • Col 居中可以使用 {offset: 2, span: 8} 这种形式.
  • 块级元素可以采用 .block-center 属性.
  • 不过还是设定 width, 然后 margin: 0 auto 更实用一点.

grid system

container

container class should be added to the root element where you would like to use bootstrap.

container-fluid is for full-width container.

row

from the official documentation:

  • Content should be placed within columns, and only columns may be immediate children of rows.
  • Columns create gutters (gaps between column content) via padding. That padding is offset in rows for the first and last column via negative margin on .rows.
  • Grid classes apply to devices with screen widths greater than or equal to the breakpoint sizes, and override grid classes targeted at smaller devices. grid classes are col-xs-*, col-sm-*, col-md-*, col-lg-*.

| | phone | tablet | desktop | large desktop |
| ————— | ———– | ———- | ———- | ————- |
| Container width | None (auto) | 750px | 970px | 1170px |
| Class prefix | .col-xs- | .col-sm- | .col-md- | .col-lg- |

The gutter width for bootstrap is 15px for each column, 30px between.

generally, you should use the col-sm-N family of classes. such that it applies for all devices except phones.

use col-xs-offset-* classes to offset the columns.

use col-xs-pull-* classes to pull column to left, and push to push to right.

bootstrap 默认已经采用了 border-box 模式。如果需要使用 content-box 需要自己指定。

jQuery and un-jQuery

jQuery

$el.append(htmlString)
$el.prepend(htmlString)

$newEl.insertBefore(queryString)
$newEl.insertAfter(queryString)

un-jQuery

DOM 操作

选择器可以使用 querySelector 和 querySelectorAll, 但是这两个的性能太差了,最好使用 getElementById, getElementsByTagName, getElementsByClassName

$el.val() 对应 el.value

获取 attr

$el.attr('id');
el.getAttribute('id);
el.setAttribute(attr, value)

parent and children

$el.children
$el.parent()
el.children
el.parentNode

设置 css

$el. css({color: '#000'});
el.style.color = '#000';
$el.css('color');
window.getComputedStyle(el)['color'];

特别地
$(el).hide(); // jQuery
el.style.display = ‘none’; // native
$(el).show(); // jQuery
el.style.dispaly = ”

设置 class

$el.addClass(className) // remove, has, toggle
el.classList.add(className) // remove, contains, toggle

设置 text
Get text
// jQuery
$el.text();
// Native
el.textContent;

Set text
// jQuery
$el.text(string);

// Native
el.textContent = string;

设置 html

Get HTML
// jQuery
$el.html();

// Native
el.innerHTML;
Set HTML
// jQuery
$el.html(htmlString);

// Native
el.innerHTML = htmlString;

inserting html fragment to document

Have a look at insertAdjacentHTML

var element = document.getElementById("one");
var newElement = '<div id="two">two</div>'
element.insertAdjacentHTML( 'afterend', newElement )
// new DOM structure: <div id="one">one</div><div id="two">two</div>
position is the position relative to the element you are inserting adjacent to:

beforebegin Before the element itself

afterbegin Just inside the element, before its first child

beforeend Just inside the element, after its last child

afterend After the element itself

事件

绑定事件
$el.on(eventName, eventHandler); // jQuery
el.addEventListener(eventName, eventHandler); // Native

解绑事件
$el.off(eventName, eventHandler); // jQuery
el.removeEventListener(eventName, eventHandler); // Native

触发事件
// 用户事件
$(el).trigger(‘custom-event’, {key1: ‘data’}); // jQuery
// Native, note that
const event = new CustomEvent(‘custom-event’, {detail: {key1: ‘data’}});
el.dispatchEvent(event);

// 原生事件
$(el).trigger(‘change’);
var ev = new Event(“look”, {“bubbles”:true, “cancelable”:false});
document.dispatchEvent(ev);

ajax

getJSON
jQuery
$.getJSON(‘/my/url’, function(data) {
});
IE9+
var request = new XMLHttpRequest();
request.open(‘GET’, ‘/my/url’, true);
request.onload = function() {
if (request.status >= 200 && request.status < 400) {
// Success!
var data = JSON.parse(request.responseText);
} else {
// We reached our target server, but it returned an error
}
};
request.onerror = function() {
// There was a connection error of some sort
};
request.send();
Post
jQuery
$.ajax({
type: ‘POST’,
url: ‘/my/url’,
data: data
});
IE8+
var request = new XMLHttpRequest();
request.open(‘POST’, ‘/my/url’, true);
request.setRequestHeader(‘Content-Type’, ‘application/x-www-form-urlencoded; charset=UTF-8’);
request.send(data);
Request
jQuery
$.ajax({
type: ‘GET’,
url: ‘/my/url’,
success: function(resp) {
},
error: function() {
}
});
IE9+
var request = new XMLHttpRequest();
request.open(‘GET’, ‘/my/url’, true);
request.onload = function() {
if (request.status >= 200 && request.status < 400) {
// Success!
var resp = request.responseText;
} else {
// We reached our target server, but it returned an error
}
};
request.onerror = function() {
// There was a connection error of some sort
};
request.send();

DOM Ready

https://github.com/oneuijs/You-Dont-Need-jQuery/blob/master/README.zh-CN.md
http://youmightnotneedjquery.com/

JavaScript DOM API

document object

attributes of document object

document.title               // 设置文档标题等价于 HTML 的<title>标签
document.URL                 // 设置 URL 属性从而在同一窗口打开另一网页
document.fileCreatedDate     // 文件建立日期,只读属性
document.fileModifiedDate    // 文件修改日期,只读属性
document.fileSize            // 文件大小,只读属性
document.cookie              // 设置和读出 cookie
document.charset             // 设置字符集 简体中文:gb2312
document.body         // body 元素
document.location.hash/host/href/port          // location

methods of document object

getElementById() // 返回一个 Element
getElementsByName() // 根据 name 属性获得元素,返回一个 NodeList
getElementsByTagName() // 返回一个 HTMLCollection/NodeList(Webkit)
getElementsByClassName() // 返回一个 HTMLCollection
querySelector() // 返回一个符合的元素,性能很差
querySelectorAll() // 返回所有符合的元素组成的 NodeList, 性能很差
document.write()
document.createElement()

window object

functions in window object

setTimeout(func, milliseconds, parameters...)
setInterval(func, milliseconds, parameters...)

NOTE: javascript is asynchonous, even if you set 0 timeout, the function is just put into the execute queue, not invoked immediately.

window.location

location    setting location will cause the page to redirect to new page
location.href
location.protocol
location.host
location.hostname
location.port
location.pathname
location.search
location.hash
location.assign()    go to a new address
location.replace()   go to a new address and do not disturb the history
location.reload()   reload the page

window.history

history.back()
history.forward()
history.go(number)

window.screen

window.screen.width screen width, not the viewport width
window.screen.height screen height

alert, confirm and prompt

alert show a message
confirm return a bool by user action
prompt

Same Origin Policy

document.domain is the key to decide the origin of a script.

scripts under different subdomain can set the their document.domain to a same domain, and then then can share the same cookie or communicate

Cross-Origin Resource Sharing