用JSONP實現模擬百度搜索自動補全下拉(不用ajax)

某一天 我只是想模擬實現一下百度下拉搜索 結果發現小小的一點代碼 居然可以有這么多的知識點 涉及到JSONP 以及 事件委托 還有如何實現在點擊頁面其他地方的時候實現下拉框的收起...生命在于折騰

代碼地址,歡迎star~~~~??

(●'?'●) 不用后臺請求 不用ajax請求 直接使用百度的jsonp請求地址進行請求就可以啦~~~(●'?'●)

1 先看結果

test.gif

2 涉及知識點

  • JSONP 是什么???
  • 事件委托的使用。??
  • 點擊其他位置實現下拉框收起。??

3 知識點詳解

  • JSONP

我們都知道 在使用ajax請求的時候 跨域這種情況是要另外處理的 那JSONP就是可以解決這種問題的。具體是使用什么原理呢?你想一下,平時我們在頁面中引入script標簽之后,script標簽也有相關的url發起請求,而且可以請求任何有效地址的數據,正是因為這個漏洞,我們可以發起跨域請求。那么機智的程序員們,就這么想,我可不可以將需要用到的JSON 數據填充回來.所以就有了JSONP。

JSONP執行之后,會返回包含json編碼數據的響應體,也就是會自動執行。
當通過<script>元素調用數據時,響應內容必須是用js函數名和圓括號包裹起來,而不是發這樣的json數據。

[1,2,{"name": "katherine"}]

它會發送一個這樣被js包裹后的JSON響應

handleResponse (
[1,2,{"name": "katherine"}]
)

所以我們可以事先在js代碼中定義一個類似handleResponse的函數,這樣我們就可以在響應獲取到這些JSON響應之后,通過事先定義好的函數,對傳過來的數據為所欲為了。

其實說簡單一點,JSONP就是在你向頁面填充某個url的<script>標簽之后,返回一個可以執行的函數名,同時是有帶參數的函數名。所以只要我們事先定義好這個函數的相關操作就可以了。

當然,如果這個函數名是定死的話,那就不好玩了,所以一般支持JSONP的服務不會強制指定客戶端必須實現的回調函數名,如果每次函數名都一定是handleResponse,豈不是很受限。所以,一般會用查詢參數的值,允許客戶端指定一個函數名,然后使用函數名去填充響應。也就是說我們在這個script標簽的url給定地址中填充類似 ?cb=callbackname來告訴服務端,我們這邊客戶端需要什么樣的函數名。

這里推薦一個覺得講得很好的地址:談談JSON和JSONP

后面我在實際應用中也會說到。

  • 事件委托

在這個例子中,需要實現一個就是,當有下拉熱詞出現的時候,其實是包裹來一個ul內部的,而這些填充的很多的li,每一個li都需要綁定一個click事件,用來干什么呢?當點擊其中一個li的時候,需要將這個li的內容填充到搜索輸入框。

因為這些li是動態生成的,不可能每一個li都去綁定點擊事件。而且這樣對于性能消耗也很大,所以,可以使用事件冒泡的特點,將點擊件綁定在父級元素ul上。

然后通過事件的屬性,event.target來獲取當前真正被點擊的元素,然后就可以獲取這個被點擊元素的內容啦。??

因為篇幅有限 我也沒有講得很詳細 傳送門: 事件委托和事件代理

4 實際代碼

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>Title</title>
    <body>
        <div id="search-arr"><input type="text" id="searchInput" type="search" placeholder="search"><ul id="selectItem"></ul></div>
    </body>
  </head>
</html>
  <script>
    // 執行JSONP操作的函數,設置添加script.
// 這個看不懂的話 建議參考一下js權威指南 第507頁
      function getJSON (url,callback) {
        // 因為百度的那個請求的地址是 https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?wd=迷糊查詢參數名&cb=回調函數名
        // 所以這里會每次給getJSON 函數添加一個
        var cbnum = "cb" + getJSON.counter++  // 假設這里就是cb1
        var cbname = "getJSON." + cbnum  // 這里的函數名字就是 getJSON.cb1
        url+="&cb="+ cbname    // 將這個回調函數名添加 到url后面
        var script = document.createElement("script")   // 創建一個script標簽
        getJSON[cbnum] = function(response) {   // 給這個getJSON函數添加一個getJSON.cb1的函數屬性。這也就是我們的回調函數
          try {
            callbackfn(response)    // 對獲取到的response數據進行處理的回調函數
          }
          finally {   // 記得哦 執行完函數之后 要刪除這個添加的屬性  同時刪除這個script標簽 不然查詢次數增加之后  會越來越多
            delete getJSON[cbname]
            script.parentNode.removeChild(script)
          }
        }
        script.src = url   // 添加url屬性
        document.body.appendChild(script)
      }
      getJSON.counter = 0   // 初始化一個counter的值
      // 對獲取到的JSONP數據進行處理的回調函數
      function callbackfn(data) {
        var selectList = document.getElementById("selectItem")
        // 可以通過視頻中看到,數據的結構中的關鍵字存在data.s中
        if (data.s.length !== 0) {   
          let newList = data.s.map((item) => `<li><a target="_blank" >${item}</a></li>`) 
          let newStr = newList.join('')
          selectList.innerHTML = newStr
          selectList.style.display = 'block'
        }
      }
    window.onload = function () {
      var target = document.getElementById("searchInput")
      var selectList = document.getElementById("selectItem")
      AutoCompleteInput()
      //  定義一個隱藏下拉單的函數
      function hideList (selectList) {
        selectList.innerHTML = ''
        selectList.style.display = 'none'
      }
      // 對輸入框進行監聽 數據變化就執行getJSONP函數,請求新的關鍵字
      function AutoCompleteInput() {
        target.addEventListener('input', () => {
          let oldValue = ''
          let newValue = target.value
          if (newValue !== '' && newValue !== oldValue) {
            getJSON('https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?wd='+(newValue))
          } else {
            hideList(selectList)
          }
          oldValue = target.value
        })
        // 點擊除了下拉框和輸入框之外的其他地方 要隱藏下拉框
        document.addEventListener('click', (e) => {
          // 這里使用了contains,contaisn可以知道某個元素是否是其子元素
          if(selectList.contains(e.target)||e.target.getAttribute('id') !== 'searchInput') {
            hideList(selectList)
          }
        })
        target.addEventListener('keydown', () => {
          hideList(selectList)
        })
        // 事件代理部分代碼 
        selectList.addEventListener('click', (e) => {
          e.preventDefault()   // 我為了防止點擊之后跳轉到搜索頁面,所以寫了這句,你可以去掉,就可以跳轉到搜索結果頁面了
          let selectValue = ''
          if (e.target && e.target.nodeName == "A") {
            selectValue = e.target.innerHTML  
          } else {
            selectValue = e.target.getElementByTagName('a').innerHTML
          }
          target.value = selectValue
        })
      }
}
  </script>
<style>
* {
  padding:0;
  margin:0;
  box-sizing:border-box;
}
#search-arr {
  position:relative;
  width:200px;
  margin:50px auto;
}
#searchInput {
  outline: none;
  width:200px;
  height:30px;
  border:1px solid rgba(109,207,246,.5);
  border-radius:25px;
  padding:10px 10px 10px 20px;
  background:#ededed;
  transition:all ease .4s;
}
#searchInput:focus {
  /* width:250px; */
  box-shadow: 0 0 5px rgba(109,207,246,.5);
  background:white;
}
ul#selectItem {
  display: none;
  position:absolute;
  top:31px;
  width:100%;
  border:1px solid gray;
  border-top:none;
}
li {
  width:100%;
  list-style-type:none;
  background:white;
  color:black;
  /* padding:10px; */
  cursor:pointer;
}
li:hover {
    background: #ededed;
}
a {
  display: inline-block;
  text-decoration: none;
  color:black;
  width:100%;
  padding:1px 5px;
}
</style>

5 遺留問題

1 JSONP請求的錯誤處理: 我想了這個問題,像我們在執行ajax請求的時候,是可以有相應狀態碼告訴我們發生錯誤的,404啊,之類的。但是這個JSONP請求我們要怎么知道錯誤呢?怎么處理錯誤呢?
2 JSONP請求之后動態添加script標簽,需要移除這些新添加的script標簽,那是不是也會有漏洞或者會有問題呢?

??然后我驚訝地發現,有人的問題和我一樣,我就。。。。 遺留問題
我看了答案還不是很理解。
會的歡迎評論喲~~~??

代碼地址,歡迎star~~~~??

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,763評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,238評論 3 428
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,823評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,604評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,339評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,713評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,712評論 3 445
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,893評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,448評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,201評論 3 357
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,397評論 1 372
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,944評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,631評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,033評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,321評論 1 293
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,128評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,347評論 2 377

推薦閱讀更多精彩內容

  • 第一章 入門 基本功能:訪問和操作 dom 元素,控制頁面樣式,對頁面的事件處理,與ajax完美結合,有豐富的插件...
    X_Arts閱讀 1,062評論 0 2
  • <a name='html'>HTML</a> Doctype作用?標準模式與兼容模式各有什么區別? (1)、<...
    clark124閱讀 3,531評論 1 19
  • AJAX 原生js操作ajax 1.創建XMLHttpRequest對象 var xhr = new XMLHtt...
    碧玉含香閱讀 3,251評論 0 7
  • 如果遇到了那個愛的人,不妨就去愛一生吧。反正一生又不長,反正愛會一直生長! 有些生命,眼睛看不清楚,鏡頭放大N多倍...
    美娜寶閱讀 248評論 0 0
  • ? 是否有過遇到不爽的情況,想到放棄的時候。這時該不該放棄呢? ? 是否有過特別開心的時候買了好多東西。這些東西是...
    Hygea閱讀 696評論 0 0