36. jQuery 事件

weixin_34253539發表於2018-11-06

事件

事件處理中最頭疼的就是瀏覽器相容問題,jQuery封裝了很好的API,可以方便的進行事件處理。

在1.7之前的版本中 jQuery 處理事件有多個方法, (google 搜尋: jquery live bind degelate)作用各不相同,後來統一的使用 on/off 方法。

.on( events [,selector ] [,data ], handler(eventObject) )

各個引數的意思:

  1. events:一個或多個空格分隔的事件型別和可選的名稱空間,或僅僅是名稱空間,比如 "click", "keydown.myPlugin", 或者 ".myPlugin"
  2. selector:一個選擇器字串,用於過濾出被選中的元素中能觸發事件的後代元素。如果選擇器是 null 或者忽略了該選擇器,那麼被選中的元素總是能觸發事件
  3. data:當一個事件被觸發時,要傳遞給事件處理函式的 event.data
  4. handler(eventObject):事件被觸發時,執行的函式。若該函式只是要執行 return false 的話,那麼該引數位置可以直接簡寫成 false

例子:

<div class="box">
  <ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
  </ul>
</div>
<input id="ipt" type="text"> <button id="btn">新增</button>
<div id="wrap">
</div>

<script>
$('.box li').on('click', function(){
    console.log(1)
  var str = $(this).text()
  $('#wrap').text(str)
})

//等同於
$('.box>ul>li').click(function(){
    console.log(2)
  var str = $(this).text()
  $('#wrap').text(str)
})

//也可以這樣寫
$('.box li').on('click.hello', function(){
    console.log(3)
  var str = $(this).text()
  $('#wrap').text(str)
})
//名稱空間沒什麼特別的作用,只不過在解綁事件時便於區分繫結的事件
$('.box li').off('click.hello')

//可是用如下方法新增的元素是沒繫結事件的
$('#btn').on('click', function(){
  var value = $('#ipt').val()
  $('.box>ul').append('<li>'+value+'</li>')
})

//我們可以用事件代理
$('.box ul').on('click', 'li', function(){
  var str = $(this).text()
  $('#wrap').text(str)
})

//上面程式碼相當於原生 js 的
document.querySelector('.box ul').addEventListener('click', function(e){
    if(e.target.tagName.toLowerCase() === 'li'){
        //do something
    }
})

//繫結事件的時候我們也可以給事件附帶些資料,只不過這種用法很少見
$('.box').on('click', {name: 'hunger'}, function(e){
    console.log(e.data)
})

.one( events [, selector ] [, data ], handler(eventObject) )

同 on,繫結事件,但只執行一次

.off( events [, selector ] [, handler ] )

移除一個事件處理函式

$('.box li').off('click')

.trigger( eventType [, extraParameters ] )

根據繫結到匹配元素的給定的事件型別執行所有的處理程式和行為

$('#foo').on('click', function() {
  console.log($(this).text())
});
$('#foo').trigger('click')

其它

jQuery還提供了一些常見事件的快捷方式

3624093-4a39146724e79996.jpg

jQuery 動畫

基礎

.hide([duration ] [,easing ] [,complete ])

用於隱藏元素,沒有引數的時候等同於直接設定 display 屬性

看幾個例子

 $('.target').hide();
// 等同於 $('.target').css('display', 'none')
$('#book').hide(300, function() {
  alert('Animation complete.');
})
  $('#book').hide(300, 'linear', function() {
    alert('Animation complete.');
  })

.show( [duration ] [, easing ] [, complete ] )

用於顯示元素,用法和hide類似

.toggle( [duration ] [, easing ] [, complete ] )

事件處理套件也有一個名為.toggle()方法。哪一個被呼叫取決於傳遞的引數的設定

用來切換元素的隱藏、顯示,類似於toggleClass,用法和showhide類似

漸變

.fadeIn( [duration ] [, easing ] [, complete ] )

通過淡入的方式顯示匹配元素,引數含義和上面相同

$('#book').fadeIn('slow', function() {
// Animation complete
});

.fadeOut( [duration ] [, easing ] [, complete ] )

通過淡出的方式隱藏匹配元素

$('#book').fadeOut('slow', function() {
// Animation complete.
});

.fadeTo( duration, opacity [, easing ] [, complete ] )

調整匹配元素的透明度,方法通過匹配元素的不透明度做動畫效果

$('#book').fadeTo('slow', 0.5, function() {
  // Animation complete.
});

.fadeToggle( [duration ] [, easing ] [, complete ] )

通過匹配的元素的不透明度動畫,來顯示或隱藏它們,方法執行匹配元素的不透明度動畫。當被可見元素呼叫時,元素不透明度一旦達到0,display樣式屬性設定為none ,所以元素不再影響頁面的佈局。

$("button:first").click(function() {
  $("p:first").fadeToggle("slow", "linear");
});

滑動

.slideDown( [duration ] [, easing ] [, complete ] )

用滑動動畫顯示一個匹配元素,方法將給匹配元素的高度的動畫,這會導致頁面的下面部分滑下去,彌補了顯示的方式

$('#book').slideDown('slow', function() {
    // Animation complete.
});

.slideUp( [duration ] [, easing ] [, complete ] )

用滑動動畫隱藏一個匹配元素,方法將給匹配元素的高度的動畫,這會導致頁面的下面部分滑上去,當一個隱藏動畫後,高度值達到0的時候,display 樣式屬性被設定為none,以確保該元素不再影響頁面佈局。

$('#book').slideUp('slow', function() {
    // Animation complete.
});

.slideToggle( [duration ] [, easing ] [, complete ] )

用滑動動畫顯示或隱藏一個匹配元素,方法將給匹配元素的高度的動畫,這會導致頁面中,在這個元素下面的內容往下或往上滑。display 屬性值儲存在 jQuery 的資料快取中,所以 display 可以方便以後可以恢復到其初始值。

如果一個元素的 display 屬性值為 inline,然後是隱藏和顯示,這個元素將再次顯示 inline。當一個隱藏動畫後,高度值達到 0 的時候,display 樣式屬性被設定為 none,以確保該元素不再影響頁面佈局。

$('#clickme').click(function() {
  $('#book').slideToggle('slow', function() {
 // Animation complete.
  });
});

動畫佇列

$box.hide(1000, function(){
   $box.show(1000, function(){
     $box.fadeOut('slow',function(){
       $box.fadeIn('slow',function(){
         $box.slideUp(function(){
           $box.slideDown(function(){
             console.log('動畫執行完畢')
           })
         })
       })
     })
   })
})
//等價於
$box.hide(1000)
    .show(1000)
    .fadeOut()
    .fadeIn()
    .slideUp()
    .slideDown(function(){
      console.log('真的完畢了')
    })

自定義動畫

上面幾個簡單的動畫不能滿足需求的時候,jquery提供了自定義動畫行為的方法

.animate( properties [, duration ] [, easing ] [, complete ] )

官方文件

properties 是一個CSS屬性和值的物件,動畫將根據這組物件移動。

$('#clickme').click(function() {
  $('#book').animate({
    opacity: 0.25,
    left: '+=50',
    height: 'toggle'
  }, 5000, function() {
    // Animation complete.
  });
});

.clearQueue

清除動畫佇列中未執行的動畫

.finish

停止當前動畫,並清除動畫佇列中所有未完成的動畫,最終展示動畫佇列最後一幀的最終狀態

.stop( [clearQueue ] [, jumpToEnd ] )

停止當前正在執行的動畫

  • clearQueue(default: false)
  • jumpToEnd(default: false)

http://js.jirengu.com/viqiv/1/edit?html,css,output

參考

jQuery API


Ajax

jQuery.ajax( [settings ] )

先看個例子

$.ajax({
    url: 'xxx.php',
    method: 'GET',
    data: {
        name: 'Byron',
        age: 24,
        sex: 'Male'
    }
}).done(function(result){

    console.log(result);

}).fail(function(jqXHR, textStatus){

    consloe.log(textStatus);

});

這樣我們就傳送了一個 get 請求

方法提供了幾個常用的 setting

  1. async:預設設定下,所有請求均為非同步請求(也就是說這是預設設定為 true )。如果需要傳送同步請求,請將此選項設定為 false

  2. beforeSend:請求傳送前的回撥函式,用來修改請求傳送前jqXHR物件,此功能用來設定自定義 HTTP 頭資訊,等等。該jqXHR和設定物件作為引數傳遞

  3. cache:如果設定為 false ,瀏覽器將不快取此頁面。注意: 設定 cache 為 false 將在 HEAD和GET請求中正常工作。它的工作原理是在GET請求引數中附加 "_={timestamp}"

  4. context:這個物件用於設定 Ajax 相關回撥函式的上下文。 預設情況下,這個上下文是一個 ajax 請求使用的引數設定物件

  5. data:傳送到伺服器的資料。將自動轉換為請求字串格式。GET 請求中將附加在 URL 後面,POST請求作為表單資料

  6. headers:一個額外的{鍵:值}對對映到請求一起傳送。此設定會在 beforeSend 函式呼叫之前被設定 ;因此,請求頭中的設定值,會被 beforeSend 函式內的設定覆蓋

  7. method:HTTP 請求方法 (比如:"POST", "GET ", "PUT",1.9之前使用 “type”)

瞭解了這些引數,使用 jQuery 處理 ajax 請求就簡單了

$.ajax({
  method: "POST",
  url: "some.php",
  data: { name: "John", location: "Boston" }
}).done(function( msg ) {
  alert( "Data Saved: " + msg );
});

除了這個方法,jQuery還提供了一些額外的方法

jQuery.get( [settings] ) / jQuery.post( [settings ] )

這兩個方法專門用來處理getpost請求

$.ajax({
  url: url,
  data: data,
  success: success,
  dataType: dataType
});

$.ajax({
  type: "POST",
  url: url,
  data: data,
  success: success,
  dataType: dataType
});

dataType:從伺服器返回的預期的資料型別。預設:智慧猜測(xml, json, script, 或 html)

jQuery.getJSON( url [, data ] [, success(data, textStatus, jqXHR) ] )

使用一個 HTTP GET 請求從伺服器載入 JSON 編碼的資料,這是一個 Ajax 函式的縮寫,這相當於:

$.ajax({
  dataType: "json",
  url: url,
  data: data,
  success: success
});

.load( url [, data ] [, complete(responseText, textStatus, XMLHttpRequest) ] )

從伺服器載入資料並且將返回的 HTML 程式碼並插入至 匹配的元素中

$('#result').load('ajax/test.html')

.serialize() / serializeArray()

將用作提交的表單元素的值編譯成字串,方法沒有引數,使用標準的 URL-encoded 符號上建立一個文字字串. 它可以對一個代表一組表單元素的 jQuery 物件進行操作,比如<input>, <textarea>, 和 <select>:

<form id="holder">
  <input type="text" name="a" value="1"/>
  <div>
    <input type="text" name="b" value="2" id="b" />
  </div>
  <input type="hidden" name="c" value="3" id="c" />
  <div>
    <input type="checkbox" name="f" value="8" checked="true"/>
    <input type="checkbox" name="f" value="9" checked="true"/>
  </div>
</form>

$("#holder").serialize(); //a=1&b=2&c=3&f=8&f=9

$("#holder").serializeArray();
/*
    [
      {name: 'a', value: '1'},
      {name: 'b', value: '2'},
      {name: 'c', value: '3'},
      {name: 'f', value: '8'},
      {name: 'f', value: '9'}
    ]
*/

serialize 和 serializeArray 都是針對 jQuery 物件(選中的 from 元素)進行操作,只是返回值格式不同而已。這裡特別要注意:這2個 API 只能操作 form,如果將 holder 改成div,會發現不起作用。


jsonp

jsonp 全稱是 JSON with Padding,是為了解決跨域請求資源而產生的解決方案。很多時候我們需要在客戶端獲取伺服器資料進行操作,一般我們會使用 ajax+webservice 做此事,但是如果我們希望獲取的資料和當前頁面並不是一個域,著名的同源策略(不同域的客戶端指令碼在沒明確授權的情況下,不能讀寫對方的資源)會因為安全原因決絕請求,也就是我們不能向其它域直接傳送請求以獲取資源。

在 localhot 域上有一個 books.php,裡面包含指令碼對 test.com 域的 books.php 傳送 get 請求,希望獲取其 book 列表資源,這就是一個跨域請求資源

$.ajax({
type:'get',
url:'http://test.com/books.php'

});

頁面會報一個這樣的錯誤:XMLHttpRequest cannot load http://test.com/books.php. Origin http://localhostis not allowed by Access-Control-Allow-Origin.

jsonp 是為了解決這個問題出現的。

jsonp原理

雖然有同源策略的限制,但是並不是HTML上所有資源都必須是同一個域的,我們常見的頁面為了節省流量或載入速度採用 Google 或微軟的 jQuery CDN,在頁面上我們可以這樣寫就可以引用 jQuery 了

<script  type="text/javascript"
    src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js">
</script>

iframe、img、style、script 等元素的 src 屬性可以直接向不同域請求資源,jsonp 正式利用 script 標籤跨域請求資源的。

簡單實現

localhost 的 books.php 希望獲得域 test.com 的 books列表,在域 test.com 內 book 列表儲存在 books.xml 中

test.com/books.xml

<?xml version="1.0"?>
<books>
    <book name="JavaScript: The Defiitive Guide" publisher="O'Reilly Media, Inc.">
        <author>David Flanagan</author>
    </book>
    <book name="PHP anf MySQL Web Development" publisher="Perason Education">
        <author>Luke Welling</author>
        <author>Laura Thomson</author>
    </book>
    <book name="HTTP: The Defiitive Guide" publisher="O'Reilly Media, Inc.">
        <author>David Courley</author>
        <author>Brian Totty</author>
    </book>
</books>

明顯 JavaScript 不能直接獲取 books.xml,在 test.com 中需要有一個機制將 xml 轉化為 json(這也就是為什麼叫 jsonp,其實和 ajax 一樣,返回的資料不一定是 json 格式,只是 json 很好用),並動態拼接一條 javascript 呼叫語句返回,這個例子中直接使用 php 頁面拼接

test.com/bookservice.php

<?php
    $path=$_SERVER["DOCUMENT_ROOT"].'/books.xml';
    $json=json_encode(simplexml_load_file($path));

    $callbackFn=$_GET['callback'];
    echo "$callbackFn($json);";
?>

這樣首先把 xml 檔案內容轉換成一個 json 物件

{"book":[
    {"@attributes":{"name":"JavaScript: The Defiitive Guide","publisher":"O'Reilly Media, Inc."},"author":"David Flanagan"},
    {"@attributes":{"name":"PHP anf MySQL Web Development","publisher":"Perason Education"},"author":["Luke Welling","Laura Thomson"]},
    {"@attributes":{"name":"HTTP: The Defiitive Guide","publisher":"O'Reilly Media, Inc."},"author":["David Courley","Brian Totty"]}
]}

然後拼接為一條 javascript 語句交給 localhost 去處理,當然 test.com 並不知道應該拼接的方法名叫什麼,需要 localhost 在傳送請求的時候在 url 中傳入一個叫 callback(這個也隨便,兩邊同步就行)的引數指明。看看 localhost 怎麼傳送請求吧

localhost/books.php

<!DOCTYPE html>
<html>
<head>
    <title>Books</title>
    <?php include('/components/headerinclude.php');?></head>
    <style type="text/css">
        .book-title
        {
            font-size: 15px;
            font-weight:bold;
            margin-top:6px;
        }

        .book-info
        {
            color:#ccc;
            font-style:italic;
            border-bottom:dashed 1px #ccc;
        }
    </style>
</head>
<body>
    <div style="margin:20px;">
        <div style="font-size:16px;font-weight:bold;">Books</div>
        <div id="books">

        </div>
    </div>
</body>
</html>

我們希望在 id 為 books 的 div 中展示所有 book,先新增一個用以顯示 book 的 javascript 函式,也就是獲取到資料後的回撥函式,結合上面拼接的 json 格式可以這麼寫

function displayBooks(books){
    var books=books.book;
    var booksContainer=document.getElementById('books');
    for(var i=0;i<books.length;i++){
        var tmp=Array();
        tmp.push('<div class="book-title">'+books[i]['@attributes'].name+'</div>');
        tmp.push('<div class="book-info">');
        tmp.push('<div>Publisher: '+books[i]['@attributes'].publisher+'</div>');
        tmp.push('<div>Author(s): ');
        if(typeof books[i].author=='string'){
            tmp.push(books[i].author);
        }else{
            var authors=books[i].author;
            for(var j=0;j<authors.length;j++){
                tmp.push(authors[j]+'&emsp;');
            }
        }
        tmp.push('</div>'); //end of author
        tmp.push('</div>'); //end of book info
        booksContainer.innerHTML+=tmp.join('');
    }
}

然後是關鍵的 jsonp 請求的方法了

function getBooks(){
    var script=document.createElement('script');
    script.setAttribute('type','text/javascript');
    script.setAttribute('src','http://test.com/bookservice.php?callback=displayBooks');
    document.body.appendChild(script);
}

getBooks();

在 getbooks() 方法中動態建立了一個 script 標籤,設定其 src 為 test.com 提供的獲取資料的 service 介面並傳入回撥函式,這樣我們可以看看頁面的反應,在 Chrome 控制檯下可以看到這條請求

3624093-3c17e886482c42d9.png

這樣我們就可以在 localhost 下獲取 test.com 的 books 了

3624093-029bfd8246e56b74.png

jquery 實現

在 jquery 中也有對 jsonp 的封裝,不過 jquery 把其放到了 ajax 中,不明白為什麼,畢竟這東西和 ajax 不太一樣。寫一個 jQuery 版的

function getBooks(){
    $.ajax({
        type:'get',
        url:'http://test.com/bookservice.php',
        dataType:'jsonp',
        jsonp:'callback',
        jsonpCallback:'displayBooks'
    });
}

看起來完全一樣,不過方便了很多,不用自己建立 script 標籤神馬的了,指明 dataType 為 jsonp ,回撥函式不放在 url 內了,而是使用兩個引數分別指明。

安全性問題

當然使用 jsonp 會在一定程度上造成安全性問題,如果請求的站點不是信任站點,那麼可能會在返回的方法呼叫中包含一些惡意程式碼。所以儘量向信任的站點傳送請求。另外 xss 也經常會利用 jsonp 向站點注入惡意程式碼。

相關文章