前端代碼規範的筆記(一)

爲了方便團隊其餘人員的閱讀本人代碼,減小團隊交流成本,提升工做效率。代碼規範很重要,公司這段時間尤爲對這一塊重視。css

本人對這塊的學習改正重點爲以下:html

 


 

HTMLhtml5

一,class和ID的命名問題:java

1.應該之內容和功能命名,不要以表現形式命名。node

2.字母爲小寫,多個字母時用中劃線-分隔jquery

3.ID爲js hook,避免使用空的class樣式git

二,ID統一使用雙引號github

三,標籤語意化web

 

標籤 語義
<p> 段落
<h1> <h2> <h3> ... 標題
<ul> 無序列表
<ol> 有序列表
<blockquote> 大段引用
<cite> 通常引用
<b> 爲樣式加粗而加粗
<storng> 爲強調內容而加粗
<i> 爲樣式傾斜而傾斜
<em> 爲強調內容而傾斜
code 代碼標識
abbr 縮寫

能夠看看這個百度文庫裏面的優秀文件:https://wenku.baidu.com/view/0a8d3774f242336c1eb95ea9.html正則表達式

四,HEAD

HEAD模版<!DOCTYPE html>

<html lang="zh-cmn-Hans">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <title>Style Guide</title>
  
   <!-- SEO優化 -->
<meta name="description" content="不超過150個字符"> <meta name="keywords" content=""> <meta name="author" content="name, email@gmail.com"> <!-- 爲移動設備添加 viewport --> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- iOS 圖標 --> <link rel="apple-touch-icon-precomposed" href="/apple-touch-icon-57x57-precomposed.png"> <link rel="alternate" type="application/rss+xml" title="RSS" href="/rss.xml" /> <link rel="shortcut icon" href="path/to/favicon.ico"> </head>

1.語言屬性的設置

<!-- 中文 -->
<html lang="zh-Hans">

<!-- 簡體中文 -->
<html lang="zh-cmn-Hans">

<!-- 繁體中文 -->
<html lang="zh-cmn-Hant">

<!-- English -->
<html lang="en">

2.字符編碼

utf-8

3.IE兼容模式

<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">

4.SEO優化 (搜索引擎優化)

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <!-- SEO -->
    <title>Style Guide</title>
    <meta name="keywords" content="your keywords">
    <meta name="description" content="your description">
    <meta name="author" content="author,email address">
</head>

5.viewport

<meta name="viewport" content="width=device-width, initial-scale=1.0">
  • viewport: 通常指的是瀏覽器窗口內容區的大小,不包含工具條、選項卡等內容;
  • width: 瀏覽器寬度,輸出設備中的頁面可見區域寬度;
  • device-width: 設備分辨率寬度,輸出設備的屏幕可見寬度;
  • initial-scale: 初始縮放比例;
  • maximum-scale: 最大縮放比例;

6.favicon

link指定favicon.ico文件

<link rel="shortcut icon" href="path/to/favicon.ico">

 


 

CSS

良好的註釋是很是重要的。請留出時間來描述組件(component)的工做方式、侷限性和構建它們的方法。不要讓你的團隊其它成員 來猜想一段不通用或不明顯的代碼的目的。

彙總:

  • 以 Components 的角度思考,以兩個單詞命名(.screenshot-image
  • Components 中的 Elements,以一個單詞命名(.blog-post .title
  • Variants,以中劃線-做爲前綴(.shop-banner.-with-icon
  • Components 能夠互相嵌套
  • 記住,你能夠經過繼承讓事情變得更簡單

一,class和ID命名

  • 使用語義化、通用的命名方式;
  • 使用連字符 - 做爲 ID、Class 名稱界定符,不要駝峯命名法和下劃線;
  • 避免選擇器嵌套層級過多,儘可能少於 3 級;
  • 避免選擇器和 Class、ID 疊加使用;

避免class,ID和選擇器混合式使用,若改了class名稱還要去改CSS代碼,不利於後期的維護

二,聲明塊格式

  • 選擇器分組時,保持獨立的選擇器佔用一行;
  • 聲明塊的左括號 { 前添加一個空格;
  • 聲明塊的右括號 } 應單獨成行;
  • 聲明語句中的 : 後應添加一個空格;
  • 聲明語句應以分號 ; 結尾;
  • 通常以逗號分隔的屬性值,每一個逗號後應添加一個空格;
  • rgb()rgba()hsl()hsla() 或 rect() 括號內的值,逗號分隔,但逗號後不添加一個空格;
  • 對於屬性值或顏色參數,省略小於 1 的小數前面的 0 (例如,.5 代替 0.5-.5px 代替 -0.5px);
  • 十六進制值應該所有小寫和儘可能簡寫,例如,#fff 代替 #ffffff
  • 避免爲 0 值指定單位,例如,用 margin: 0; 代替 margin: 0px;

三,聲明順序

相關屬性應爲一組,推薦的樣式編寫順序

  1. Positioning(因爲定位(positioning)能夠從正常的文檔流中移除元素,而且還能覆蓋盒模型(box model)相關的樣式,所以排在首位)
  2. Box model(盒模型決定了組件的尺寸和位置,所以排在第二位)
  3. Typographic(排版相關)
  4. Visual(視覺)
  5. Other(其餘)

四,引號的使用

  1. url() 、屬性選擇符、屬性值使用雙引號。

五,媒體查詢(Media query)的位置

  將媒體查詢放在儘量相關規則的附近。不要將他們打包放在一個單同樣式文件中或者放在文檔底部。若是你把他們分開了,未來只會被你們遺忘。

六,不要使用 @import

七,Components 最少以兩個單詞命名,經過 - 分離

  • 點贊按鈕 (.like-button)
  • 搜索框 (.search-form)
  • 文章卡片 (.article-card)

八,Elements 是 Components 中的元素 Elements 命名就用一個單詞;

 .search-form {
    > .field { /* ... */ }
    > .action { /* ... */ }
  }

對於假若須要兩個或以上單詞表達的 Elements 類名,不該使用中劃線和下劃線鏈接,應直接鏈接

  .profile-box {
    > .firstname { /* ... */ }
    > .lastname { /* ... */ }
    > .avatar { /* ... */ }
  }

任什麼時候候儘量使用 classnames。標籤選擇器在使用上沒有問題,可是其性能上稍弱,而且表意不明確。避免使用標籤選擇器。

假若你須要爲組件設置定位,應將在組件的上下文(父元素)中進行處理,好比如下例子中,將 widths 和 floats 應用在 list component(.article-list) 當中,而不是 component(.article-card) 自身。

當出現多個嵌套的時候容易失去控制,應保持不超過一個嵌套

關於CSS的性能優化

一,慎重選擇高消耗的樣式

  • box-shadows
  • border-radius
  • transparency(透明)
  • transforms(動畫)
  • CSS filters(濾鏡 性能殺手)

二,避免重排列

  • width
  • height
  • padding
  • margin
  • display
  • border-width
  • position
  • top
  • left
  • right
  • bottom
  • font-size
  • float
  • text-align
  • overflow-y
  • font-weight
  • overflow
  • font-family
  • line-height
  • vertical-align
  • clear
  • white-space
  • min-height

三,正確的使用display屬性

Display 屬性會影響頁面的渲染,請合理使用。

  • display: inline後不該該再使用 width、height、margin、padding 以及 float;

  • display: inline-block 後不該該再使用 float;

  • display: block 後不該該再使用 vertical-align;

  • display: table-* 後不該該再使用 margin 或者 float;

四,不濫用float

五,動畫性能的優化

動畫的基本概念:

  • 幀:在動畫過程當中,每一幅靜止畫面即爲一「幀」;
  • 幀率:即每秒鐘播放的靜止畫面的數量,單位是fps(Frame per second);
  • 幀時長:即每一幅靜止畫面的停留時間,單位通常是ms(毫秒);
  • 跳幀(掉幀/丟幀):在幀率固定的動畫中,某一幀的時長遠高於平均幀時長,致使其後續數幀被擠壓而丟失的現象。

通常瀏覽器的渲染刷新頻率是 60 fps,因此在網頁當中,幀率若是達到 50-60 fps 的動畫將會至關流暢,讓人感到溫馨。

  • 若是使用基於 javaScript 的動畫,儘可能使用 requestAnimationFrame. 避免使用 setTimeout, setInterval.
  • 避免經過相似 jQuery animate()-style 改變每幀的樣式,使用 CSS 聲明動畫會獲得更好的瀏覽器優化。
  • 使用 translate 取代 absolute 定位就會獲得更好的 fps,動畫會更順滑。

六,多利用硬件能力,如經過 3D 變形開啓 GPU 加速

通常在 Chrome 中,3D或透視變換(perspective transform)CSS屬性和對 opacity 進行 CSS 動畫會建立新的圖層,在硬件加速渲染通道的優化下,GPU 完成 3D 變形等操做後,將圖層進行復合操做(Compesite Layers),從而避免觸發瀏覽器大面積重繪和重排。

七,提高CSS選擇器的性能

理解了CSS選擇器從右到左匹配的機制後,明白只要當前選擇符的左邊還有其餘選擇符,樣式系統就會繼續向左移動,直到找到和規則匹配的選擇符,或者由於不匹配而退出。咱們把最右邊選擇符稱之爲關鍵選擇器

一、避免使用通用選擇器

二、避免使用標籤或 class 選擇器限制 id 選擇器

三、避免使用標籤限制 class 選擇器

四、避免使用多層標籤選擇器。使用 class 選擇器替換,減小css查找

/* Not recommended */
treeitem[mailfolder="true"] > treerow > treecell {}
/* Recommended */
.treecell-mailfolder {}

五、避免使用子選擇器

/* Not recommended */
treehead treerow treecell {}
/* Recommended */
treehead > treerow > treecell {}
/* Much to recommended */
.treecell-header {}

六、使用繼承

/* Not recommended */
#bookmarkMenuItem > .menu-left { list-style-image: url(blah) }
/* Recommended */
#bookmarkMenuItem { list-style-image: url(blah) }

 


 

 

JavaScript

一,註釋

原則:

  • As short as possible(如無必要,勿增註釋):儘可能提升代碼自己的清晰性、可讀性。
  • As long as necessary(若有必要,儘可能詳盡):合理的註釋、空行排版等,可讓代碼更易閱讀、更具美感。

1. 單行註釋

必須獨佔一行。// 後跟一個空格,縮進與下一行被註釋說明的代碼一致。

2. 多行註釋

避免使用 /*...*/ 這樣的多行註釋。有多行註釋內容時,使用多個單行註釋。

3. 函數/方法註釋

  1. 函數/方法註釋必須包含函數說明,有參數和返回值時必須使用註釋標識。;
  2. 參數和返回值註釋必須包含類型信息和說明;
  3. 當函數是內部函數,外部不可訪問時,可使用 @inner 標識;
/**
 * 函數描述
 *
 * @param {string} p1 參數1的說明
 * @param {string} p2 參數2的說明,比較長
 *     那就換行了.
 * @param {number=} p3 參數3的說明(可選)
 * @return {Object} 返回值描述
 */
function foo(p1, p2, p3) {
    var p3 = p3 || 10;
    return {
        p1: p1,
        p2: p2,
        p3: p3
    };
}

4. 文件註釋

文件註釋用於告訴不熟悉這段代碼的讀者這個文件中包含哪些東西。 應該提供文件的大致內容, 它的做者, 依賴關係和兼容性信息。以下:

/**
 * @fileoverview Description of file, its uses and information
 * about its dependencies.
 * @author user@meizu.com (Firstname Lastname)
 * Copyright 2009 Meizu Inc. All Rights Reserved.
 */

二,命名

變量

  1. 變量使用‘駝峯’命名法
  2. 私有屬性、變量和方法如下劃線 _ 開頭。
  3. 常量, 使用所有字母大寫,單詞間下劃線分隔的命名方式。

函數

  函數:

  1. 函數, 使用 Camel 命名法。
  2. 函數的參數, 使用 Camel 命名法。

  類

  1. , 使用 Pascal 命名法
  2. 類的 方法 / 屬性, 使用 Camel 命名法

  枚舉屬性

  1. 枚舉變量 使用 Pascal 命名法。
  2. 枚舉的屬性, 使用所有字母大寫,單詞間下劃線分隔的命名方式。

  由多個單詞組成的 縮寫詞,在命名中,根據當前命名法和出現的位置,全部字母的大小寫與首字母的大小寫保持一致。

三,命名語法

  1. 類名,使用名詞。
  2. 函數名,使用動賓短語。
  3. boolean 類型的變量使用 is 或 has 開頭。
  4. Promise 對象用動賓短語的進行時表達。

四,接口命名規範

  1. 可讀性強,見名曉義;
  2. 儘可能不與 jQuery 社區已有的習慣衝突;
  3. 儘可能寫全。不用縮寫,除非是下面列表中約定的;(變量以表達清楚爲目標,uglify 會完成壓縮體積工做)
經常使用詞 說明
options 表示選項,與 jQuery 社區保持一致,不要用 config, opts 等
active 表示當前,不要用 current 等
index 表示索引,不要用 idx 等
trigger 觸點元素
triggerType 觸發類型、方式
context 表示傳入的 this 對象
object 推薦寫全,不推薦簡寫爲 o, obj 等
element 推薦寫全,不推薦簡寫爲 el, elem 等
length 不要寫成 len, l
prev previous 的縮寫
next next 下一個
constructor 不能寫成 ctor
easing 示動畫平滑函數
min minimize 的縮寫
max maximize 的縮寫
DOM 不要寫成 dom, Dom
.hbs 使用 hbs 後綴表示模版
btn button 的縮寫
link 超連接
title 主要文本
img 圖片路徑(img標籤src屬性)
dataset html5 data-xxx 數據接口
theme 主題
className 類名
classNameSpace class 命名空間

五,True 和 False 布爾表達式

類型檢測優先使用 typeof。對象類型檢測使用 instanceof。null 或 undefined 的檢測使用 == null。

下面的布爾表達式都返回 false:

  • null
  • undefined
  • '' 空字符串
  • 0 數字0

但當心下面的, 可都返回 true:

  • '0' 字符串0
  • [] 空數組
  • {} 空對象

六,不要在 Array 上使用 for-in 循環

for-in 循環只用於 object/map/hash 的遍歷, 對 Array 用 for-in 循環有時會出錯. 由於它並非從 0 到 length - 1 進行遍歷, 而是全部出如今對象及其原型鏈的鍵值。

// Not recommended
function printArray(arr) {
  for (var key in arr) {
    print(arr[key]);
  }
}

printArray([0,1,2,3]);  // This works.

var a = new Array(10);
printArray(a);  // This is wrong.

a = document.getElementsByTagName('*');
printArray(a);  // This is wrong.

a = [0,1,2,3];
a.buhu = 'wine';
printArray(a);  // This is wrong again.

a = new Array;
a[3] = 3;
printArray(a);  // This is wrong again.

// Recommended
function printArray(arr) {
  var l = arr.length;
  for (var i = 0; i < l; i++) {
    print(arr[i]);
  }
}

七,二元和三元操做符

操做符始終寫在前一行, 以避免分號的隱式插入產生預想不到的問題。

var x = a ? b : c;

var y = a ?
    longButSimpleOperandB : longButSimpleOperandC;

var z = a ?
        moreComplicatedB :
        moreComplicatedC;

. 操做符也是如此:

var x = foo.bar().
    doSomething().
    doSomethingElse();

八,條件(三元)操做符(?:)

三元操做符用於替代 if 條件判斷語句。

// Not recommended
if (val != 0) {
  return foo();
} else {
  return bar();
}

// Recommended
return val ? foo() : bar();

九,&& 和 ||

二元布爾操做符是可短路的, 只有在必要時纔會計算到最後一項。

// Not recommended
function foo(opt_win) {
  var win;
  if (opt_win) {
    win = opt_win;
  } else {
    win = window;
  }
  // ...
}

if (node) {
  if (node.kids) {
    if (node.kids[index]) {
      foo(node.kids[index]);
    }
  }
}

// Recommended
function foo(opt_win) {
  var win = opt_win || window;
  // ...
}

var kid = node && node.kids && node.kids[index];
if (kid) {
  foo(kid);
}

 


 

 

jQuery規範

一,使用最新版本的 jQuery

最新版本的 jQuery 會改進性能和增長新功能,若不是爲了兼容舊瀏覽器,建議使用最新版本的 jQuery。如下是三條常見的 jQuery 語句,版本越新,性能越好

$('.elem')
$('.elem', context)
context.find('.elem')

 分別使用 1.4.二、1.4.四、1.6.2 三個版本測試瀏覽器在一秒內可以執行多少次,結果 1.6.2 版執行次數遠超兩個老版本。

二,jQuery 變量

  1. 存放 jQuery 對象的變量以 $ 開頭;
  2. 將 jQuery 選擇器返回的對象緩存到本地變量中複用;
  3. 使用駝峯命名變量;
var $myDiv = $("#myDiv");
$myDiv.click(function(){...});

三,選擇器

  1. 儘量的使用 ID 選擇器,由於它會調用瀏覽器原生方法 document.getElementById 查找元素。固然直接使用原生 document.getElementById 方法性能會更好;
  2. 在父元素中選擇子元素使用 .find() 方法性能會更好, 由於 ID 選擇器沒有使用到 Sizzle 選擇器引擎來查找元素;
// Not recommended
var $productIds = $("#products .class");

// Recommended
var $productIds = $("#products").find(".class");

四,DOM 操做

  1. 當要操做 DOM 元素的時候,儘可能將其分離節點,操做結束後,再插入節點;
  2. 使用字符串鏈接或 array.join 要比 .append()性能更好;
var $myList = $("#list-container > ul").detach();
//...a lot of complicated things on $myList
$myList.appendTo("#list-container");
// Not recommended
var $myList = $("#list");
for(var i = 0; i < 10000; i++){
    $myList.append("<li>"+i+"</li>");
}

// Recommended
var $myList = $("#list");
var list = "";
for(var i = 0; i < 10000; i++){
    list += "<li>"+i+"</li>";
}
$myList.html(list);

// Much to recommended
var array = [];
for(var i = 0; i < 10000; i++){
    array[i] = "<li>"+i+"</li>";
}
$myList.html(array.join(''));

五,事件

  1. 若是須要,對事件使用自定義的 namespace,這樣容易解綁特定的事件,而不會影響到此 DOM 元素的其餘事件監聽;
  2. 對 Ajax 加載的 DOM 元素綁定事件時儘可能使用事件委託。事件委託容許在父元素綁定事件,子代元素能夠響應事件,也包括 Ajax 加載後添加的子代元素;
$("#myLink").on("click.mySpecialClick", myEventHandler);
$("#myLink").unbind("click.mySpecialClick");
// Not recommended
$("#list a").on("click", myClickHandler);

// Recommended
$("#list").on("click", "a", myClickHandler);

六,鏈式寫法

  1. 儘可能使用鏈式寫法而不是用變量緩存或者屢次調用選擇器方法;
  2. 當鏈式寫法超過三次或者由於事件綁定變得複雜後,使用換行和縮進保持代碼可讀性;
$("#myDiv").addClass("error").show();
$("#myLink")
  .addClass("bold")
  .on("click", myClickHandler)
  .on("mouseover", myMouseOverHandler)
  .show();

七,其餘

  1. 多個參數使用對象字面量存儲;
  2. 不要將 CSS 寫在 jQuery 裏面;
  3. 正則表達式僅準用 .test() 和 .exec() 。不許用 "string".match() ;

八,jQuery插件模版

// jQuery Plugin Boilerplate
// A boilerplate for jumpstarting jQuery plugins development
// version 1.1, May 14th, 2011
// by Stefan Gabos

// remember to change every instance of "pluginName" to the name of your plugin!
(function($) {

    // here we go!
    $.pluginName = function(element, options) {

        // plugin's default options
        // this is private property and is  accessible only from inside the plugin
        var defaults = {

            foo: 'bar',

            // if your plugin is event-driven, you may provide callback capabilities
            // for its events. execute these functions before or after events of your
            // plugin, so that users may customize those particular events without
            // changing the plugin's code
            onFoo: function() {}

        }

        // to avoid confusions, use "plugin" to reference the
        // current instance of the object
        var plugin = this;

        // this will hold the merged default, and user-provided options
        // plugin's properties will be available through this object like:
        // plugin.settings.propertyName from inside the plugin or
        // element.data('pluginName').settings.propertyName from outside the plugin,
        // where "element" is the element the plugin is attached to;
        plugin.settings = {}

        var $element = $(element), // reference to the jQuery version of DOM element
             element = element;    // reference to the actual DOM element

        // the "constructor" method that gets called when the object is created
        plugin.init = function() {

            // the plugin's final properties are the merged default and
            // user-provided options (if any)
            plugin.settings = $.extend({}, defaults, options);

            // code goes here

        }

        // public methods
        // these methods can be called like:
        // plugin.methodName(arg1, arg2, ... argn) from inside the plugin or
        // element.data('pluginName').publicMethod(arg1, arg2, ... argn) from outside
        // the plugin, where "element" is the element the plugin is attached to;

        // a public method. for demonstration purposes only - remove it!
        plugin.foo_public_method = function() {

            // code goes here

        }

        // private methods
        // these methods can be called only from inside the plugin like:
        // methodName(arg1, arg2, ... argn)

        // a private method. for demonstration purposes only - remove it!
        var foo_private_method = function() {

            // code goes here

        }

        // fire up the plugin!
        // call the "constructor" method
        plugin.init();

    }

    // add the plugin to the jQuery.fn object
    $.fn.pluginName = function(options) {

        // iterate through the DOM elements we are attaching the plugin to
        return this.each(function() {

            // if plugin has not already been attached to the element
            if (undefined == $(this).data('pluginName')) {

                // create a new instance of the plugin
                // pass the DOM element and the user-provided options as arguments
                var plugin = new $.pluginName(this, options);

                // in the jQuery version of the element
                // store a reference to the plugin object
                // you can later access the plugin and its methods and properties like
                // element.data('pluginName').publicMethod(arg1, arg2, ... argn) or
                // element.data('pluginName').settings.propertyName
                $(this).data('pluginName', plugin);

            }

        });

    }

})(jQuery);

九,性能優化

1,避免沒必要要的 DOM 操做

瀏覽器遍歷 DOM 元素的代價是昂貴的。最簡單優化 DOM 樹查詢的方案是,當一個元素出現屢次時,將它保存在一個變量中,就避免屢次查詢 DOM 樹了。

// Recommended
var myList = "";
var myListHTML = document.getElementById("myList").innerHTML;

for (var i = 0; i < 100; i++) {
  myList += "<span>" + i + "</span>";
}

myListHTML = myList;

// Not recommended
for (var i = 0; i < 100; i++) {
  document.getElementById("myList").innerHTML += "<span>" + i + "</span>";
}

2,緩存數組的長度

循環無疑是和 JavaScript 性能很是相關的一部分。經過存儲數組的長度,能夠有效避免每次循環從新計算。

注: 雖然現代瀏覽器引擎會自動優化這個過程,可是不要忘記還有舊的瀏覽器。

var arr = new Array(1000),
    len, i;
// Recommended - size is calculated only 1 time and then stored
for (i = 0, len = arr.length; i < len; i++) {

}

// Not recommended - size needs to be recalculated 1000 times
for (i = 0; i < arr.length; i++) {

}

3,異步加載第三方內容

當你沒法保證嵌入第三方內容好比 Youtube 視頻或者一個 like/tweet 按鈕能夠正常工做的時候,你須要考慮用異步加載這些代碼,避免阻塞整個頁面加載。

(function() {

    var script,
        scripts = document.getElementsByTagName('script')[0];

    function load(url) {
      script = document.createElement('script');
      script.async = true;
      script.src = url;
      scripts.parentNode.insertBefore(script, scripts);
    }

    load('//apis.google.com/js/plusone.js');
    load('//platform.twitter.com/widgets.js');
    load('//s.widgetsite.com/widget.js');

}());

4,避免使用jQuery實現動畫

  1. 禁止使用 slideUp/Down() fadeIn/fadeOut() 等方法;
  2. 儘可能不使用 animate() 方法;

 


 

 

移動端優化

移動Web問題小結

http://www.alloyteam.com/2015/06/yi-dong-web-wen-ti-xiao-jie/

移動端統計

https://github.com/jtyjty99999/mobileTech

關於無線端Web解決方案

http://am-team.github.io/about/about.html

click 的300ms延遲響應

click 事件由於要等待雙擊確認,會有 300ms 的延遲,體驗並非很好。

開發者大多數會使用封裝的 tap 事件來代替click 事件,所謂的 tap 事件由 touchstart 事件 + touchmove 判斷 + touchend 事件封裝組成。

相關文章
相關標籤/搜索