前端

JavaScript 可视化库调研

核心关注指标:

  1. 好看吗?
  2. 支持多少数据,性能如何,内存占用如何?
  3. 开发活跃度
  4. 能否交互
  5. 是否支持 react
  6. 渲染后端是什么,基于 SVG 还是 canvas 还是 HTML?
  7. License,GPL 的不能要
  8. 支持绘制图形的种类

综合

  1. Vega Vega 很全面,几乎包括了所有的图形样式。
  2. nivo 基于 react 和 d3。支持的图形不少。
  3. echarts 百度出品,国内用的比较多,但是感觉有点丑。据说 bug 也比较多。

统计常用图

绝大多数的图还是画折线图这些的,大部分的库也是做这个的。

  1. recharts 基于 React 和 D3.js。使用 SVG,只支持 line chart,bar chart 这些比较常见的。
  2. reactviz 基于 react,Uber 出品,也是常见的统计图
  3. chartist 亮点是有动画,没有依赖,体积特别小。支持的图比较少
  4. nvd3 这个看起来确实不错,支持的图表类型一般,基于 d3.js。
  5. chart.js 支持的数量也比较少,主要是 line chart 和 bar chart. 这个可能是标星最多的了。
  6. xkcd 风格的图表

以下为不推荐的库:

  1. apexchart 似乎是 fusion chart 的一个开源版本。https://github.com/apexcharts/apexcharts.js
  2. uvcharts.js 开发很不活跃,才 200 个星星
  3. victory 没看出有什么特别吸引人的。
  4. chartbuilder https://github.com/Quartz/Chartbuilder。好几年没有更新了。而且比较丑。
  5. c3.js https://c3js.org/examples.html。基于 D3, 貌似图比较少。
  6. toast https://ui.toast.com/。韩国的一个东西,还包含了日历。

图(Graph)

这里的图指的是计算机科学上的图,也就是由节点和边构成的结构。

  1. sigma 用于绘制 graph 的 http://sigmajs.org/
  2. cytoscape https://js.cytoscape.org/

金融

  1. Lightweight Charts。比较小巧,适合绘制金融数据。
  2. http://dygraphs.com/gallery/。这个貌似只是画线条图的
  3. uplot。特点是非常小,不支持任何交互。主要是画时间序列的。
  4. dc.js。特点是支持 crossfilter。特别好看,不过支持的图不多。

其他

  1. plotly, plotly 是一个 Python 和 JavaScript 的绘图库。
  2. https://github.com/antvis/g2plot
  3. https://github.com/antvis/G2

Python 和 JavaScript 语法对比

命名

  1. 注意使用驼峰变量名,不要使用下划线变量名

字符串

格式化

JavaScript:

`hello ${name}`

Python:

f"hello {name}"

文件

打开文件:

JavaScript:

const fs = require(fs).promises;  // 使用 async/await 版本的 fs 模块

await fs.writeFile(filename, data);  // 写入文件

// 文件是否存在
try {
  await fs.stat(filename)
  exitst = true
} catch (e) {
  exists = false
}

Python:

with open(filename, w) as f:
    f.write(data)

os.path.exists(filename)  # 文件是否存在

数组

切片:

JavaScript:

const arr1 = arr2.slice(3, 5);

Python:

arr1 = arr2[3:5]

CSS 布局基础知识

布局

网页的布局是面向文档流的,也就是每个元素默认都是从左到右,从上到下依次排列的。当然就像文章一样,有些元素比如标题默认就会另起一行,并且单独占据这一行。

所有的元素都分为三类:inline、inline-block、和 block。

其中 block 元素占据了一行的位置,即使他们的宽度不够一行,并且他们有自己的宽度和高度,比如 h1 元素。

inline-block 结合了 inline 和 block 元素的特性,首先他布局是 inline 的,也就是不会另起一行,但是又可以设定单独的高度和宽度。

display: none 将会完全不渲染该元素,visibility: hidden 会渲染这个元素,只是在该显式的地方留下空白

使用 inline-box 的布局

定位

CSS 中元素的定位有如下几种,可以使用 position 指定

| 方法 | 说明 |
|———-|—————————————————————————–|
| absolute | behaves like fixed, but relative to nearest non-static ancestor |
| fixed | fixed to the viewport, if set(top, left, bottom, right) |
| float | floated element will become a block element, but it will not occupy one row |
| relative | relative to its static positions, if set(top, left, bottom, right) |
| static | 默认的定位方法,指的是在文档中的位置是静态的 |

使用 float 的布局

后续元素没有占据指定空间

元素浮动之后,它后面的元素就会去占据它的位置,然而我们往往并不想影响到后面的元素,所以应该指定它后面的元素清除浮动。

not stretching parent element
floating elements will also not stretching the element containing it, to fix that, add overflow: auto to the parent element

盒模型

讨厌的 content-box 模型

width set width of the content, and

  • padding will push out the border…
  • background-color only set for content area
  • entire width is for border

符合直觉的 border-box 模型

as shown by the picture, border-box width sets the entire width, contains border + padding + content

如下图所示,border-box 模型设定的快读包含了 border + padding + content

responsive design

query device width

@media screen and (mid/max-width: xxxpx)

set viewport

<meta name="viewport" content="width=device-width/320, initial-scale=0.5, maximum-scale=1, minimum-scale=3, user-scalable=no" />

记录 CSS 的一个坑

在 td 元素上无法使用 width

可以通过指定: table-layout: fixed 解决

centering

margin: 0 auto;

css columns

column-count: x;
column-gap: xpx;

通过 js 获得最终 CSS 属性

https://developer.mozilla.org/zh-CN/docs/Web/API/Window/getComputedStyle

reference

[1] http://learnlayout.com

『译』 CSS 选择器备忘录(cheatsheet)

这两天打算写写爬虫,准备用 css 选择器来抽取内容,翻译了一篇文章,正好复习一下,这篇文章遵守 CC-BY-NC-SA 协议。

大多数的 web 开发者首先学会了 CSS。因为给 HTML 元素写样式规则的元素选择器都很容易理解和记忆。经过一点点练习之后,就会发现成组的 CSS 表达式非常的有必要,比如使用一个逗号分隔开选择器,就可以把同一个样式属性附加到不同的多个元素了。

使用标签选择器和分组的选择器当然也有不足,那就是他们会应用到所有的标签。很快你就会意识到有时候需要给一个特定的元素加上样式。对于大多数开发者来说,最简单的选择当然是使用 class 和 id 选择器了。但是很遗憾的是这样下去的话,整个页面就会充满各种奇奇怪怪的类了。

除了不管的给 HTML 添加纯粹用于应用样式的 class 和 id 之外,你也可以考虑使用高级的 CSS 选择器来给指定元素应用样式。高级的选择器大致可以分类为上下文选择器、属性选择器、伪元素选择器和伪类选择器。

元素选择器

元素选择器把样式应用到所有选中元素上,也是被使用最广泛的选择器

选择器 | 语法 | 描述
——-|—–|———
元素 | h2 | 选择页面上的所有 h2 元素,h2 {text-align: center;} 会把所有 h2 元素的文本居中对齐
分组 | h1,h2,h3 | 选择页面上所有的 h1, h2, h3 元素,h1, h2, h3 {margin-bottom: .5em;} 将会把 h1, h2, h3 标签的底部 margin 设置成 0.5 em

class 和 id 选择器

Class 和 id 选择器是你能够把某些属性应用到个别元素上而不影响其他的。Class 让你能够按照概念分类。两个或者更多元素可以属于同一个组。id 给一个元素一个唯一的标识,同一个 id 每个页面上只能使用一次。

选择器 | 语法 | 描述
——-|—–|———
Class | .warning p.warning | 前者选择所有属于 warning 类的元素,后者选择所有属于 warning 类的 p 元素
id | #title | 选择 id 为 title 的元素

上下文选择器

Contextual selectors (combinators) target elements based on their relationships to other elements in the document’s tree hierarchy, and use the same parent, child, sibling, and descendant terminology.

上下文选择器基于元素在 HTML 文档中的上下级挂你来选择元素。

选择器 | 语法 | 描述
——-|—–|———
后代 | #main li | 选择一个元素的后代元素。注意这里不一定是直接子元素,后代即可。
相邻元素 | h2+p | 选择一个元素的直接相邻元素,比如说这个表达式只选择 h2 的直接相邻元素 p
子元素 | div>p| 选择所有直接子元素
所有相邻元素 | h2~p | 选择一个元素的所有相邻元素

伪类选择器

伪类选择器基于浏览器状态或者文档中的位置来选择元素。浏览器在幕后保存了好多状态,比如一个链接有没有被访问过,一个元素有没有被选中,一个元素是不是它这种类型的第一个或者最后一个元素,甚至于一个元素是不是它的父元素的第一个子元素。

所有处于同一个状态的元素被看做是属于同一个伪类,尽管在 HTML 中并没有明确声明类的名字。伪类标识符用冒号 : 开头,而且一般是和元素名称放在一起用的。

用作链接和用户状态的伪类

当给一个链接应用样式的时候,按照下面表中的顺序来制定样式才能获得预期效果。

选择器 | 语法 | 描述
——-|—–|———
未访问的链接 | :link | 选择未访问过的链接,例如 a:link
访问过的链接 | :visited | 选择已经访问过的链接,例如 a:visited
用户焦点 | :focus| 选择当前拥有用户光标,可以直接输入的元素,例如 a:focus input:focus
悬浮 | :hover | 选择当前用户鼠标指针悬浮的元素
激活 | :active | 选择当前用户正在点击的元素

用于选择相邻元素的伪类

下面的 6 个选择器用于选择拥有同一个父节点的元素中符合某种条件的元素,算是一个断言(assert)吧。

选择器 | 语法 | 描述
——-|—–|———
第一个元素 | :first-child | 如果指定的元素是他的父元素的第一个子元素,选择生效
最后一个元素 | :last-child | 如果指定的元素是他的父元素的最后一个子元素,选择生效
唯一子元素 | :only-child| 如果指定的元素是他的父元素的唯一一个子元素,选择生效
同类型第一个 | :first-of-type | 如果指定的元素是他的父元素的子元素中同类型的第一个,选择生效
同类型最后一个 | :last-of-type | 如果指定的元素是他的父元素的子元素中同类型的最后一个,选择生效
同类型唯一一个 | :only-of-type | 如果指定的元素是他的父元素的子元素中同类型的唯一一个,选择生效

随着元素和 DOM 树的更新,这些伪类能够应用的元素都是动态变化的。

CSS3 中的结构相关伪类

CSS3 提供了更灵活的选择方式,可以根据元素在文档中的位置选择一系列的元素。这些选择器还可以接受一个参数

| 语法 | 描述
|—–|———
| :nth-child(n) | 父元素的第 n 个子元素
| :nth-last-child(n) | 父元素的倒数第 n 个子元素
| :nth-of-type(n)| 父类型子元素中,同类型中的第 n 个
| :nth-last-of-type(n) | 父类型子元素中,同类型中的倒数第 n 个

其中的 n 的取值往往取如下几种

值 | 含义
——|——
odd 2n+1| 奇数
even 2n | 偶数
2 | 第二个

伪元素选择器

伪元素选择器让你能够给虚构的元素增加样式,而不用在 HTML 中真的创建他们。::first-letter::first-line 选择了文本的一部分从而避免了使用 span 元素。 ::before::after 选择器用来在并不存在的地方动态插入文本。

CSS3 uses a double colon for pseudo-elements. To provide backward compatibility for older browsers, omit one of the colons that precede the pseudo-elements (ie :first-letter)

CSS3 使用两个冒号来表示伪元素。为了兼容老浏览器,可以省略一个冒号(也就是 :first-letter

选择器 | 语法 | 描述
——-|—–|———
第一个字母 | ::first-letter | 选择指定元素的第一个字母
最后一个元素 | ::first-line | 选择指定元素的第一行
之前 | ::before| 选择指定元素之前生成的一个伪元素,和元素是相邻关系
之后 | ::after | 选择指定元素之后生成的一个伪元素,和元素是相邻关系

属性选择器

属性选择器基于元素时候有某个特定的属性而定。下面的 7 中选择器中,后面 3 中是 CSS3 中新定义的,浏览器们可能还没有完全支持。

选择器 | 语法 | 描述
——-|—–|———
存在 | E[attr] | 选择包含某个属性的元素
相等 | E[attr='val'] | 选择某个属性的值等于 xxx 的元素
包含 | E[attr~='val']| 很鸡肋,把某个元素的值按空格分隔,如果包含给定的值,选中。优点类似于类的分隔
开头 | E[attr\|='val']| 很鸡肋,某个属性的值以给定的值 + - 开头
开头 | E[attr^='val']| 某个属性的值以给定的值开头
结尾 | E[attr$='val']| 某个属性的值以给定的值结尾
包含 | E[attr*='val']| 某个属性的值包含给定的值

前端框架 Vue 学习笔记

之前在网页里小范围的用了 vue, 感觉用起来非常爽,现在打算做自己的笔记应用。这次打算做成 SPA 的形式,前端全部用 vue 来写。需要 node 的一些东西,记录下学习过程。迈向全栈,哈哈 ^_^

安装

首先,全局安装 vue:

npm install --global @vue/cli

data 属性

只有在创建时提供的 data 属性才是响应式的,在创建之后在添加新元素就不管用了。

当然还可以使用 app.$watch 方法显式创建一些监听器

// $watch 是一个实例方法
vm.$watch("a", function (newValue, oldValue) {
  // 这个回调将在 &#x60;vm.a&#x60; 改变后调用
})

methods 和 computed

methods 定义了一些可以调用的方法,他的值也可以用来插值。但是最好使用 computed,因为 computed 是有缓存的。

computed 属性也可以设置 setter,所以实际上,computed 属性相当于对现有属性的一种映射和变化。

v-bind 和 v-model

  • v-bind 用于单项绑定:在 HTML 属性中使用 v-bind 绑定,标签中使用 {{ }}。只能使用表达式,而不能使用语句。
  • v-model 用于双向绑定:在 input 这类用户可以输入的组件中,需要双向绑定,使用 v-model.

v-bind 类似的指令还有 v-once 和 v-html

v-model 实际上等价于

<input v-model="searchText">
<input
  v-bind:value="searchText"
  v-on:input="searchText = $event.target.value"
>

要想在自定义输入组件中支持 v-model 的话,就需要使用 v-bind 和 v-on 两个方法了,而不能直接使用 v-model。

v-if 和 v-for

这两个就和所有模板系统中的 if 和 for 一样。vue 中提供的额外方便之处是,可以使用 template 标签,这样就不会多一个标签了

vue 是懒渲染的,因此会尽可能地复用组件,可以使用 key 来区分

v-if 是真正的条件式渲染,v-show 则只是在切换 display 属性

vue 没有代理数组的赋值方法,所以需要使用 app.$set 方法

@

如果只是指定一个事件处理函数的话,那么参数就是 event。如果自己指定了参数的话,可以使用 $event 来代表 event

还可以使用 .prevent 和 .stop 等修饰符

component

vue 中最终要的概念就是组件了。使用组件来模块式得构建应用。需要通过 props 属性来定义组件中的属性

<div id="app-7">
  <ol>
    <!--
      现在我们为每个 todo-item 提供 todo 对象
      todo 对象是变量,即其内容可以是动态的。
      我们也需要为每个组件提供一个“key”,稍后再
      作详细解释。
    -->
    <todo-item
      v-for="item in groceryList"
      v-bind:todo="item"
      v-bind:key="item.id">
    </todo-item>
  </ol>
</div>

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

props 是一个数组,用来声明组建的属性。然后通过属性来传递。

组件还可以通过 $emit 来发送事件,这些事件可以被所有的组件监听到,就像普通的 DOM 事件一样。

声明周期函数

在固定的周期,vue 会调用的一些函数 created, mounted 等。需要注意的是,不要使用胖箭头函数。

new Vue({
  data: {
    a: 1
  },
  created: function () {
    // &#x60;this&#x60; 指向 vm 实例
    console.log("a is: " + this.a)
  }
})
// => "a is: 1"

创建 vue 应用

因为我们会直接通过*.vue文件来编写 vue 的组件,因此需要使用 webpack 打包编译。另外我们需要使用官方的 vue-router 来

vue init webpack notelet

这条命令基于 webpack 这个模板创建了 notelet 这个应用,也就是我们的笔记应用。

打开 src/router/index.js 可以看到 vue 创建的 router 的代码,其中@src目录的缩写。

import Vue from "vue"
  import Router from "vue-router"
import Hello from "@/components/Hello"

Vue.use(Router)

export default new Router({
  routes: [
    {
    ¦ path: "/",
    ¦ name: "Hello",
    ¦ component: Hello
    }
  ]
})

然后打开 src/main.js, 可以看到在里面使用了 App 来作为我们的跟组件

// The Vue build version to load with the &#x60;import&#x60; command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from "vue"
import App from "./App"
import router from "./router"

Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: "#app",
  router,
  template: "<App/>",
  components: { App }
})

执行 npm run dev, 然后打开 http://localhost:8080/ 就可以看到我们的 vue 应用了。注意,在写这篇文章的时候 node 8.x 下似乎有 bug, 导致 app.js 加载不出来,安装 6.x 就好了。该死的 node.

打开 App.vue, 也就是我们的根组件,可以看到下面的内容

<template>
  <div id="app">
  ¦ <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: "app"
}
</script>

<style>
#app {
  font-family: "Avenir", Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

注意其中的 router-view 便签,意思就是路由的内容都在 router-view 中显示。

添加一个新的组件和路由

接下来我们添加一个”关于”页面。打开src/router/index.js, 改成下面这样:

export default new Router({
  routes: [
  ¦ {
  ¦ ¦ path: "/",
  ¦ ¦ name: "Hello",
  ¦ ¦ component: Hello
  ¦ },
  ¦ {
  ¦ ¦ path: "/about",
  ¦ ¦ name: "About",
  ¦ ¦ component: About
  ¦ }
  ]
})

然后添加 src/components/About.vue 文件

<template>
  <div class="hello">
  ¦ <h1>About Notelet</h1>
  ¦ <p>This is a simple note app</p>
  </div>
</template>

<script>
export default {
  name: "About",
  data () {
  ¦ return {
  ¦ ¦ msg: "Hello Vue"
  ¦ }
  }
}
</script>

然后更改 App.vue 文件

<template>
  <div id="app">
  ¦ <router-link :to="{name: "Hello"}">Home</router-link>
  ¦ <router-link to="/about">About</router-link>
  ¦ <router-view></router-view>
  </div>
</template>

注意,我们使用 about 指向了 About 这个组件,而使用 hello 指向了 Hello 这个组件,注意其中还动态传递了参数。

[1] https://scotch.io/tutorials/getting-started-with-vue-router

xpath generator 是如何实现的?

写爬虫的话做到最后基本上最终没法自动化的就是指定要抽取的元素的 xpath 了,要定向爬一个网站的内容基本上都会归结到去找下一页和数据元素的 xpath. 如果能把 xpath 的生成交给不会写程序的运营同学来做的话,能够极大地解放程序员的生产力。

毕竟 xpath 也算是一个 DSL, 对于不会编程的同学还是有一定难度的。SQL 写得熟练的 PM 多得是,想找一个会写 xpath 的运营同学则是很困难,毕竟术业有专攻,运营需要面对的问题和我们程序猿还是有很大不同。多年的经验,感觉能教会他们 yaml 已经是极限了…

那么能不能有一个图形化的工具来生成 xpath 呢?答案显然是有的,chrome 浏览器就内置了生成 xpath 的工具,如下图所示:

chrome xpath

这幅图生成的 xpath 是://*[@id="fc_B_pic"]/ul[1]/li[1]/a[1]

然而 chrome 的 xpath 生成却有几个缺点:

  1. chrome 的 xpath 只会想上去找带有 id 的元素,而根据实际的情况,往往找到带有 class 的元素就可以保证找的 xpath 是对的了。
  2. chrome 生成的元素是尽量保证元素唯一的,也就是当你想要搞一个能能够选中多个元素的 xpath 时,chrome 无能为力,还是需要自己去改写。
  3. 另外就是生成之后不能方便的用图形工具去验证。

未完待续

html Node vs Element

  • Node vs Element

HTML Document consists of different node, element is one type of node.

  • NodeList vs HTMLCollection

NodeList is a collection of node, HTMLCollection is a collection of element.

  • HTMLCollection vs NodeList vs Array

HTMLCollection 和 NodeList 都是动态的,会随着 DOM 的变化而变化

Array 是静态的数据结构

steal focus from chrome omnibox on new tab

chrome set focus to the omni box when you create a new tab, although there is an api to replace the new tab page. you can’t steal the focus from the omni box in the new tab page simply. there are two work-arounds.

if you are creating a new tab programmatically

https://stackoverflow.com/questions/42178723/chrome-extension-creating-new-tab-and-taking-focus-to-page

if you are creating a new tab by click new tab button

https://stackoverflow.com/questions/17598778/how-to-steal-focus-from-the-omnibox-in-a-chrome-extension-on-the-new-tab-page