Header layout, search verification and AJAX automatic search prompt are encapsulated into components to improve code reusability

Keywords: Javascript JQuery css3 encoding

index.html header structure and style

Design sketch

 

Static style

Part of index.html

    <!-- head -->
    <div class="header">
        <div class="container">
            <!-- h1 Tag is for search engine optimization, indicating importance
            But don't show too much in the page -->
            <h1 class="fl"><a href="#" class="header-logo text-hidden">Mu Tao net</a></h1>
            <div class="search fl">
                <!-- Since there is no search page of its own, it is set to submit to Taobao during demonstration. Refer to Taobao settings -->
                <form action="https://s.taobao.com/search">
                    <!-- Because input Is an inline block, equivalent to display:inline-block
                    If you wrap, there will be gaps, which are usually half the size of the default font
                    Can write without wrapping, but poor readability
                    All add left float to solve -->
                    <!-- Set up name Submission -->
                    <input type="text" class="search-input fl" name="q" placeholder="Soul food" autocomplete="off">
                    <input type="submit" value="search" class="search-btn fl">
                </form>
                <ul class="search-list">
                    <li class="search-item text-ellipsis" title="111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111">111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111</li>
                    <li class="search-item text-ellipsis" title="222">222</li>
                    <li class="search-item text-ellipsis" title="333">333</li>
                </ul>
            </div>
            <div class="header-cart fr"></div>
        </div>
    </div>

New common style of search box component in common.css

/*Search box component search*/
.search{
    position: relative;
    width:679px;
    border:1px solid #cfd2d5;
}
.search-input{
    width:586px;
    height:40px;
    line-height:40px;
    background-color:#fff;
    border:none;
    padding:0 10px;
}
.search-btn{
    width:73px;
    height:40px;
    background-color:#07111b;
    color:#fff;
    line-height:40px;
    text-align: center;
    cursor:pointer;
    border:none;
}
.search-list{
    display: none;
    background-color:#fff;
    position: absolute;
    width:586px;
    top:100%;/*Height of parent container*/
    left:-1px;
    border:1px solid #cfd2d5;
    padding:0 10px;

}
.search-item{
    height:24px;
    line-height:24px;
}

New unique style in header in index.css

/*header*/
.header{
    height:124px;
    background-color: #f3f5f7;
}
.header-logo{
    display: block;
    background:url(../img/header-logo.png) no-repeat;
    width:136px;
    height:48px;
    margin-top:36px;
    margin-left:15px;
}
.header .search{
    margin-top:36px;
    margin-left:144px;
}

Import search.js file

Here we add the following differences of triggering conditions for textbox events:

Change text box content change + blur

keypress key is triggered. If the mouse does not lift the continuous key, it will be triggered continuously

keyup key release trigger, no matter what key is pressed (including up and down arrows and other wordless symbols), and the pasted text cannot be triggered

The difference between input text input and change is that it can be triggered without blur; mouse paste can also be triggered (poor compatibility: not supported under IE8)

To sum up, the best choice is input, but sometimes for compatibility, you can only choose keyup and make some constraint modifications yourself

 

View the form form submission action of Taobao Search

//s.taobao.com/search

When using the form, you need to add https: Protocol in front of Taobao

Namely: https://s.taobao.com/search

 

 

View the name attribute of Taobao search input box

name="q"

 

 

View the url address of ajax request when Taobao submits

1. Open the web address, open the console, find the network, and click JS below

2. You can use the button in the green box to clear the content below, and then write the content in the input box, and the information will come out in the place of the Name below

3. Click any one of them to go in, and the Headers on the right will appear. You can see the Request URL pasted and copied in the browser address bar.

 

 

 

 

 

 

 

Add search validation and data acquisition capabilities:

search.js

(function($){
    "use strict";

    //Verification
    var search=$(".search"),
        searchInput=search.find(".search-input"),
        searchBtn=search.find(".search-btn"),
        searchList=search.find(".search-list");

    searchBtn.on("click",function(){
        //submit The default behavior of the button is to submit the form, return false Can block default behavior
        //$.trim() Remove the spaces on both sides of the string to prevent no content submission
        if($.trim(searchInput.val())==="") return false;

    });

    //Auto completion
    searchInput.on("input",function(){
        var url = 'https://suggest.taobao.com/sug?code=utf-8&_ksTS=1484204931352_18291&callback=jsonp18292&k=1&area=c2c&bucketid=6&q='
         + encodeURIComponent($.trim(searchInput.val()));

        $.ajax({
            url:url,
            dataType:"jsonp",//jsonp For cross domain
            success:function(data){
                console.log(data);
            },
            error:function(data){
                console.log(error);
            }
        });

    });

})(jQuery)

Because jQuery.ajax returns the jqXHR object, which is a superset of the browser's native XMLHttpRequest object, in order to make the callback function name uniform and easy to use in $.ajax, jqXHR provides.error(),.success(),.complete()

Due to the version upgrade, there are three corresponding methods, namely. fail(),. done(),. always()

Use done(), fail(), always() to avoid code nesting in ajax and facilitate reading

So $. ajax can be modified as follows to avoid callback asynchronously:

        // Asynchronous avoid callback
        $.ajax({
            url:url,
            timeout:1,//The common cause of failure is timeout, which is set to 1ms for demonstration
            dataType:"jsonp"
        }).done(function(data){//Successful implementation
            console.log(data);
        }).fail(function(){//Failure execution
            console.log("fail");
        }).always(function(){//Always execute
            console.log("always");
        });

There are many reasons for failure, of which timeout is a very common one

To demonstrate timeout, set timeout:1 (1 ms)

 

$. trim(searchInput.val()) is the utf-8 encoding used by default

If the page is in another encoding format, such as gbk, reading data may fail due to encoding problems

Therefore, encodeuriccomponent() is used to solve the coding problem

1. The encodeuriccomponent (URIstring) function encodes a string as a URI component. The return value is a copy of URIstring. Some characters in the string will be replaced by a hexadecimal escape sequence. In short, the function is to encode, to be recognized by the background, and the background development language has corresponding decoding api, so that the data can be successfully returned.

2. The encoding of web page will affect the encoding of data when sending request, so it is necessary to encode when it is inconsistent.

        var url = 'https://suggest.taobao.com/sug?code=utf-8&_ksTS=1484204931352_18291&callback=jsonp18292&k=1&area=c2c&bucketid=6&q='
         + encodeURIComponent($.trim(searchInput.val()));

Generate pull-down data structure

(function($){
    "use strict";

    //Verification
    var search=$(".search"),
        searchInput=search.find(".search-input"),
        searchBtn=search.find(".search-btn"),
        searchList=search.find(".search-list");

    searchBtn.on("click",function(){
        //submit The default behavior of the button is to submit the form, return false Can block default behavior
        //$.trim() Remove the spaces on both sides of the string to prevent no content submission
        if($.trim(searchInput.val())==="") return false;

    });

    //Auto completion
    searchInput.on("input",function(){
        var url = 'https://suggest.taobao.com/sug?code=utf-8&_ksTS=1484204931352_18291&callback=jsonp18292&k=1&area=c2c&bucketid=6&q='
         + encodeURIComponent($.trim(searchInput.val()));

        // Asynchronous avoid callback
        $.ajax({
            url:url,
            dataType:"jsonp"
        }).done(function(data){//Successful implementation
            //console.log(data["result"]);

            var html="";
            var dataNum=data["result"].length;//Actual data volume
            var maxNum=10;//Maximum display data
            if(dataNum===0) searchList.hide().html("");

            for(var i=0;i<dataNum;i++){
                if(i>=maxNum) break;
                //console.log(data["result"][i][0]);
                html+='<li class="search-item text-ellipsis" title="'+data["result"][i][0]+'">'+data["result"][i][0]+'</li>';
            }
            searchList.html(html).show();

        }).fail(function(){//Failure execution
            searchList.hide().html("");
        }).always(function(){//Always execute
            console.log("always");
        });

    });

})(jQuery)

By the way, comment out the previous part in index.html

                <ul class="search-list">
                    <!-- <li class="search-item text-ellipsis" title="111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111">111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111</li>
                    <li class="search-item text-ellipsis" title="222">222</li>
                    <li class="search-item text-ellipsis" title="333">333</li> -->
                </ul>

Effect

 

 

Event agent and show hidden drop-down layer

(function($){
    "use strict";

    //Verification
    var search=$(".search"),
        searchForm=search.find(".search-form"),
        searchInput=search.find(".search-input"),
        searchBtn=search.find(".search-btn"),
        searchList=search.find(".search-list");

    searchForm.on("submit",function(){
        //return false You can block the default behavior, that is, form submission
        //$.trim() Remove the spaces on both sides of the string to prevent no content submission
        if($.trim(searchInput.val())==="") return false;

    });

    //Auto completion
    searchInput.on("input",function(){
        var url = 'https://suggest.taobao.com/sug?code=utf-8&_ksTS=1484204931352_18291&callback=jsonp18292&k=1&area=c2c&bucketid=6&q='
         + encodeURIComponent($.trim(searchInput.val()));

        // Asynchronous avoid callback
        $.ajax({
            url:url,
            dataType:"jsonp"
        }).done(function(data){//Successful implementation
            //console.log(data["result"]);

            var html="";
            var dataNum=data["result"].length;//Actual data volume
            var maxNum=10;//Maximum display data
            if(dataNum===0) searchList.hide().html("");

            for(var i=0;i<dataNum;i++){
                if(i>=maxNum) break;
                //console.log(data["result"][i][0]);
                html+='<li class="search-item text-ellipsis" title="'+data["result"][i][0]+'">'+data["result"][i][0]+'</li>';
            }
            searchList.html(html).show();

        }).fail(function(){//Failure execution
            searchList.hide().html("");
        }).always(function(){//Always execute
            console.log("always");
        });

    });

    //jquery Event agent for
    //The event is bound to the parent element. The first parameter is the event, the second parameter is the children of the proxied elements, and the third parameter is the function
    //In function $(this)Points to the child element being proxied
    searchList.on("click",".search-item",function(){
        searchInput.val(removeHTML($(this).html()));
        searchForm.submit();
    });

    //Show hidden dropdown
    // searchInput.on("focus",function(){
    //     searchList.show();
    // }).on("blur",function(){
    //     searchList.hide();
    // });
    //The above method is not feasible, which will lead to the failure of clicking list items
    //because blur Incident and click Event conflict
    //Because when you press the mouse over an option, it's triggered input Of blur Event, which causes the list to be hidden. When clicking the list item again, the list has been hidden
    searchInput.on("focus",function(){
        searchList.show();
    }).on("click",function(){
        return false;//Prevent bubbling when clicking document
    });
    $(document).on("click",function(){
        searchList.hide();
    });


    //Remove the html Tag, otherwise it will be displayed in the search box after clicking
    function removeHTML(str){
        // Example:<input type="text" value=">" name='username' />
        // 1,The quotation mark used for the attribute in the tag may be double quotation mark or single quotation mark, so it is used to match the content outside the quotation mark^Reverse, double, single and>It can't be obtained. Everything else can be obtained.
        // 2,Match the content in quotation marks, not to quotation marks, others are OK, the quantity can be 0, that is, there is no content in the middle of quotation marks. Single and double quotes are possible, so write twice.
        // 3,Then take the three matched ones as a group, all of them need to be matched, which can be 0 in quantity, and there is no need to capture the grouped content, so we use the?:
        // 4,The outermost layer is<>
        return str.replace(/<(?:[^'">]|"[^"]*"|'[^']*')*>/g,"");
    }

})(jQuery)

Knowledge points:

Click input to display the pull-down layer, and click other places to hide the pull-down layer

This function cannot use the following code

    searchInput.on("focus",function(){
        searchList.show();
    }).on("blur",function(){
        searchList.hide();
    });

Because the blur event here conflicts with the click event
When you press the mouse on the option, the blue event of input has been triggered, which causes the list to be hidden; when you click the list item again, the list has been hidden

You need to use the following code: (note to prevent bubbling)

    //Show hidden dropdown
    searchInput.on("focus",function(){
        searchList.show();
    }).on("click",function(){
        return false;//Prevent bubbling when clicking document
    });
    $(document).on("click",function(){
        searchList.hide();
    });

Remove the regularity of html tags in the string:

    //Remove the html Tag, otherwise it will be displayed in the search box after clicking
    function removeHTML(str){
        return str.replace(/<(?:[^'">]|"[^"]*"|'[^']*')*>/g,"");
    }

Example: < input type = "text" value = "> name ='username '/ >
1. The quotation marks used for the attributes in the tag may be double quotation marks or single quotation marks, so the contents outside the matching quotation marks can be reversed with ^, double quotation marks, single quotation marks and > cannot be obtained, and others can be obtained.
2. Match the content in quotation marks, not to quotation marks, others are OK, the quantity can be 0, that is, there is no content in the middle of quotation marks. Single and double quotes are possible, so write twice.
3. Then take the three matched ones as a group, all of them need to be matched, which can be 0 in quantity, and no need to capture the contents of the group. Therefore, you use the?:
4. The outermost layer is < >

 

Object oriented encapsulation of search box function

search.js

(function($){
    "use strict";

    function Search(elem,options){
        this.elem=elem;//Already passed in jquery object
        this.options=options;

        this.form=this.elem.find(".search-form");
        this.input=this.elem.find(".search-input");
        this.list=this.elem.find(".search-list");
        
        //Binding submit event, event agent
        this.elem.on("click",".search-btn",$.proxy(this.submit,this));
        //If autocomplete is set
        if(this.options.autocomplete) this.autocomplete();
    }

    //Default parameters
    Search.defaults={
        autocomplete:false,
        url:"https://suggest.taobao.com/sug?code=utf-8&_ksTS=1484204931352_18291&callback=jsonp18292&k=1&area=c2c&bucketid=6&q=",
        css3:false,
        js:false,
        animation:"fade"
    }

    Search.prototype.submit=function(){
        if($.trim(this.input.val())==="") return false;
        this.form.submit();
    }

    Search.prototype.autocomplete=function(){
        this.input.on("input",$.proxy(this.getData,this));
        this.list.showHide(this.options);//towards showhide Component parameters, initialization

        //Show hidden dropdown
        this.input.on("focus",$.proxy(this.showList,this))
                  .on("click",function(){
                      return false;//Prevent bubbling when clicking document
                  });
        $(document).on("click",$.proxy(this.hideList,this));
    }

    Search.prototype.getData=function(){
        var self=this;

        $.ajax({
            url:this.options.url+encodeURIComponent($.trim(this.input.val())),
            dataType:"jsonp"
        }).done(function(data){
            //Send out data Data, triggering events
            self.elem.trigger("search-getData",[data,self.list]);//Data needs to be in array form
        }).fail(function(){
            //Send failure data, trigger event
            self.elem.trigger("search-noData",[self.list]);
        });    
    }

    Search.prototype.showList=function(){
        //list There's something in it
        if(this.list.children().length===0) return;
        this.list.showHide("show");//Use showhide Component show Method
    }

    Search.prototype.hideList=function(){
        this.list.showHide("hide");//Use showhide Component hide Method
    }

    Search.prototype.setInput=function(val){
        this.input.val(val);
    }

    //Plug-in form
    $.fn.extend({
        search:function(opt,value){
            return this.each(function(){
                var ui=$(this);
                var search=ui.data("search");
                //opt Is a parameter object
                var options=$.extend({},Search.defaults,ui.data(),typeof opt==="object"&&opt);
                
                //Single example: one DOM Element corresponds to an instance. If it already exists, it does not need to be instantiated repeatedly
                if(!search){
                    search=new Search(ui,options);
                    ui.data("search",search);
                }
                
                //Exposed methods for external calls
                if(typeof search[opt]==="function"){
                    search[opt](value);
                }
            });
        }
    });

})(jQuery)

index.js

// Do not expose to the global, use anonymous function to self execute
(function($){

    "use strict";
    
//menu
    //Load data before binding event display
    $(".dropdown").on("dropdown-show",function(e){

        var ui=$(this);
        var dataLoad=ui.data("load");

        if(!dataLoad) return;

        if(!ui.data("loaded")){

            var list=ui.find(".dropdown-list");
            var html="";

            setTimeout(function(){
                $.getJSON(dataLoad,function(data){
                    for(var i=0;i<data.length;i++){
                        console.log(data[i]);
                        html+='<li class="menu-item"><a href="'+data[i]["url"]+'">'+data[i]["name"]+'</a></li>';
                    }
                    list.html(html);    
                    ui.data("loaded",true);

                });                
            },500);
        }    
    });

    //Plug in call
    $(".dropdown").dropdown({
        css3:true,
        js:true
    });

//search
    var headerSearch=$("#header-search");
    var html="";
    var maxNum=10;//Maximum display data

    headerSearch.search({
        autocomplete:true,
        css3:false,
        js:false,
        animation:"fade"
    });

    //Receiving events
    headerSearch.on("search-getData",function(e,data,list){
        //console.log(e.type);
        //console.log(data);
        
        var ui=$(this);
        //Processing after data acquisition
        html=createHeaderList(data,maxNum);
        list.html(html);

        if(html){
            ui.search("showList");
        }else{
            ui.search("hideList");
        }
        
    });

    headerSearch.on("search-noData",function(e,list){
        ui.search("hideList");//Hide dropdown
        list.html("");//Empty content
    });

    headerSearch.on("click",".search-item",function(){
        headerSearch.search("setInput",$(this).text());
        headerSearch.search("submit");
    });

    //Establish header Drop down list structure of search box in
    function createHeaderList(data,maxNum){
        var html="";
        var dataNum=data["result"].length;//Actual data volume

        if(dataNum===0) return "";

        for(var i=0;i<dataNum;i++){
            if(i>=maxNum) break;
            html+='<li class="search-item text-ellipsis" title="'+data["result"][i][0]+'">'+data["result"][i][0]+'</li>';
        }
        return html;
    }

})(jQuery);

 

Next, optimize the code:

1. The above code is for DOM operation, which consumes performance. Optimization: introduce the loaded variable to judge

 

2. When sending an ajax request, you need to add a new case where the judgment data is empty

 

 

3. If the next ajax request is sent before the completion of an ajax request, the returned data cannot be determined. Optimization: terminate the previous request before the next request

 

 

4. Every time a character is entered in the search box, an ajax request will be sent, even if the interval between two requests is very short. Suggestion: judge whether to add delay according to the user's needs

 

Optimized search.js

(function($){
    "use strict";

    function Search(elem,options){
        this.elem=elem;//Already passed in jquery object
        this.options=options;

        this.form=this.elem.find(".search-form");
        this.input=this.elem.find(".search-input");
        this.list=this.elem.find(".search-list");

        this.loaded=false;//Is it loaded? html
        
        //Binding submit event, event agent
        this.elem.on("click",".search-btn",$.proxy(this.submit,this));
        //If autocomplete is set
        if(this.options.autocomplete) this.autocomplete();
    }

    //Default parameters
    Search.defaults={
        autocomplete:false,
        url:"https://suggest.taobao.com/sug?code=utf-8&_ksTS=1484204931352_18291&callback=jsonp18292&k=1&area=c2c&bucketid=6&q=",
        css3:false,
        js:false,
        animation:"fade",
        delay:200//Default 200ms delay
    }

    Search.prototype.submit=function(){
        if($.trim(this.input.val())==="") return false;
        this.form.submit();
    }

    Search.prototype.autocomplete=function(){
        var self=this;
        var timer=null;

        this.input.on("input",function(){

            if(self.options.delay){
                clearTimeout(timer);
                timer=setTimeout(function(){
                    self.getData();
                },self.options.delay);                
            }else{
                self.getData();
                //delay When it is 0, the timer does not need to be turned on
                //Because the timer is asynchronous, even if the delay is 0, it will enter the queued state and cannot be executed immediately
            }

        });

        this.list.showHide(this.options);//towards showhide Component parameters, initialization

        //Show hidden dropdown
        this.input.on("focus",$.proxy(this.showList,this))
                  .on("click",function(){
                      return false;//Prevent bubbling when clicking document
                  });
        $(document).on("click",$.proxy(this.hideList,this));
    }

    Search.prototype.getData=function(){
        var self=this;
        var inputVal=this.input.val();

        if(!inputVal) return self.elem.trigger("search-noData");

        if(this.jqXHR) this.jqXHR.abort();//Conduct ajax When requesting, terminate the previous request first
        this.jqXHR=$.ajax({
            url:this.options.url+encodeURIComponent($.trim(inputVal)),
            dataType:"jsonp"
        }).done(function(data){
            //Send out data Data, triggering events
            self.elem.trigger("search-getData",[data]);//Data needs to be in array form
        }).fail(function(){
            //Send failure data, trigger event
            self.elem.trigger("search-noData");
        }).always(function(){
            //After execution
            self.jqXHR=null;
        });    
    }

    Search.prototype.showList=function(){
        //list There's something in it
        if(!this.loaded) return;
        this.list.showHide("show");//Use showhide Component show Method
    }

    Search.prototype.hideList=function(){
        this.list.showHide("hide");//Use showhide Component hide Method
    }

    Search.prototype.setInput=function(val){
        this.input.val(val);
    }

    Search.prototype.appendHTML=function(html){
        this.list.html(html);
        this.loaded=!!html;//!!To Boolean, if html If there is content, it will be true; otherwise, it will be false
    }

    //Plug-in form
    $.fn.extend({
        search:function(opt,value){
            return this.each(function(){
                var ui=$(this);
                var search=ui.data("search");
                //opt Is a parameter object
                var options=$.extend({},Search.defaults,ui.data(),typeof opt==="object"&&opt);
                
                //Single example: one DOM Element corresponds to an instance. If it already exists, it does not need to be instantiated repeatedly
                if(!search){
                    search=new Search(ui,options);
                    ui.data("search",search);
                }
                
                //Exposed methods for external calls
                if(typeof search[opt]==="function"){
                    search[opt](value);
                }
            });
        }
    });

})(jQuery)

index.js

// Do not expose to the global, use anonymous function to self execute
(function($){

    "use strict";
    
//menu
    //Load data before binding event display
    $(".dropdown").on("dropdown-show",function(e){

        var ui=$(this);
        var dataLoad=ui.data("load");

        if(!dataLoad) return;

        if(!ui.data("loaded")){

            var list=ui.find(".dropdown-list");
            var html="";

            setTimeout(function(){
                $.getJSON(dataLoad,function(data){
                    for(var i=0;i<data.length;i++){
                        console.log(data[i]);
                        html+='<li class="menu-item"><a href="'+data[i]["url"]+'">'+data[i]["name"]+'</a></li>';
                    }
                    list.html(html);    
                    ui.data("loaded",true);

                });                
            },500);
        }    
    });

    //Plug in call
    $(".dropdown").dropdown({
        css3:true,
        js:true
    });

//search
    var headerSearch=$("#header-search");
    var html="";
    var maxNum=10;//Maximum display data

    headerSearch.search({
        autocomplete:true,
        css3:false,
        js:false,
        animation:"fade",
        delay:0
    });

    //Receiving events
    headerSearch.on("search-getData",function(e,data){
        //console.log(e.type);
        console.log(data);
        
        var ui=$(this);
        //Processing after data acquisition
        html=createHeaderList(data,maxNum);
        ui.search("appendHTML",html);

        if(html){
            ui.search("showList");
        }else{
            ui.search("hideList");
        }
        
    }).on("search-noData",function(e){

        $(this).search("hideList");//Hide dropdown
        $(this).search("appendHTML","");//Empty content

    }).on("click",".search-item",function(){

        $(this).search("setInput",$(this).text());
        $(this).search("submit");

    });

    //Establish header Drop down list structure of search box in
    function createHeaderList(data,maxNum){
        var html="";
        var dataNum=data["result"].length;//Actual data volume

        if(dataNum===0) return "";

        for(var i=0;i<dataNum;i++){
            if(i>=maxNum) break;
            html+='<li class="search-item text-ellipsis" title="'+data["result"][i][0]+'">'+data["result"][i][0]+'</li>';
        }
        return html;
    }

})(jQuery);

index.html

<!DOCTYPE html>
<html lang="zh-CN"><!-- Set simplified Chinese -->
<head>
    <meta charset="UTF-8">
    <title>index</title>
    <link rel="stylesheet" href="css/base.css">
    <link rel="stylesheet" href="css/index.css">
    <link rel="stylesheet" href="css/common.css">
    <!-- css Generally placed on DOM Before loading, prevent DOM Streaking -->
</head>
<body>
    <!-- Navigation -->
    <div class="nav-site">
        <div class="container">
            <ul class="fl">
                <li class="fl"><a href="javascript:;" class="nav-site-login">Dear, please log in</a></li>
                <li class="fl"><a href="javascript:;" class="nav-site-reg link">Free registration</a></li>
                <li class="fl"><a href="#" class="nav-site-shop link">Mobile phone shopping</a></li>
            </ul>
            <ul class="fr">
                <li class="fl dropdown menu" data-active="menu">
                    <a href="javascript:;" class="dropdown-toggle link transition">My Mu Tao<i class="dropdown-arrow iconfont transition">&#xe642;</i></a>
                    <ul class="dropdown-list dropdown-left">
                        <li class="menu-item"><a href="#">Bought baby</a></li>
                        <li class="menu-item"><a href="#">My Tracks</a></li>
                    </ul>
                </li>
                <li class="fl dropdown menu" data-active="menu">
                    <a href="javascript:;" class="dropdown-toggle link transition">Favorites<i class="dropdown-arrow iconfont transition">&#xe642;</i></a>
                    <ul class="dropdown-list dropdown-left">
                        <li class="menu-item"><a href="#">Treasure of collection</a></li>
                        <li class="menu-item"><a href="#">Collection shop</a></li>
                    </ul>
                </li>
                <li class="fl dropdown">
                    <a href="javascript:;" class="nav-site-cat link">Commodity classification</i></a>
                </li>
                <li class="fl dropdown menu" data-active="menu" data-load="js/dropdown-seller.json">
                    <a href="javascript:;" class="dropdown-toggle link transition">Seller Center<i class="dropdown-arrow iconfont transition">&#xe642;</i></a>
                    <ul class="dropdown-list dropdown-left">
                        <li class="dropdown-loading"></li>
                        <!-- <li class="menu-item"><a href="#"> free shop</a></li>
                        <li class="menu-item"><a href="#"> sold baby</a></li>
                        <li class="menu-item"><a href="#"> baby on sale</a></li>
                        <li class="menu-item"><a href="#"> seller service market</a></li>
                        <li class="menu-item"><a href="#"> seller Training Center</a></li>
                        <li class="menu-item"><a href="#">Experience Center</a></li> -->
                    </ul>
                </li>
                <li class="nav-site-service fl dropdown menu" data-active="menu">
                    <a href="javascript:;" class="dropdown-toggle link transition">Contact customer service<i class="dropdown-arrow iconfont transition">&#xe642;</i></a>
                    <ul class="dropdown-list dropdown-right">
                        <li class="menu-item"><a href="#">Bought baby</a></li>
                        <li class="menu-item"><a href="#">My Tracks</a></li>
                    </ul>
                </li>
            </ul>
        </div>
    </div>

    <!-- head -->
    <div class="header">
        <div class="container">
            <!-- h1 Tag is for search engine optimization, indicating importance
            But don't show too much in the page -->
            <h1 class="fl"><a href="#" class="header-logo text-hidden">Mu Tao net</a></h1>
            <div id="header-search" class="search fl">
                <!-- Since there is no search page of its own, it is set to submit to Taobao during demonstration. Refer to Taobao settings -->
                <form action="https://s.taobao.com/search" class="search-form">
                    <!-- Because input Is an inline block, equivalent to display:inline-block
                    If you wrap, there will be gaps, which are usually half the size of the default font
                    Can write without wrapping, but poor readability
                    All add left float to solve -->
                    <!-- Set up name Submission -->
                    <input type="text" class="search-input fl" name="q" placeholder="Soul food" autocomplete="off">
                    <input type="submit" value="search" class="search-btn fl">
                </form>
                <ul class="search-list">
                    <!-- <li class="search-item text-ellipsis" title="111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111">111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111</li>
                    <li class="search-item text-ellipsis" title="222">222</li>
                    <li class="search-item text-ellipsis" title="333">333</li> -->
                </ul>
            </div>
            <div class="header-cart fr"></div>
        </div>
    </div>

<!--     <script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script>
    <script>
        //Short circuit operation. If the jquery of cdn is not referenced successfully, the following sentence will be executed to introduce the local jquery
        //The < \ / script > tag in brackets will be used as the end tag of the current tag, so it needs to be escaped
        window.jQuery || document.write('<script src="js/jquery.js"><\/script>');
    </script> -->
    <script src="js/jquery.js"></script>
    <script src="js/transition.js"></script>
    <script src="js/showhide.js"></script>
    <script src="js/dropdown.js"></script>
    <script src="js/search.js"></script>
    <script src="js/index.js"></script>
</body>
</html>

Finally, optimized data caching

Idea: store the acquired data into variables, cookie s, or local storage: session storage / local storage (HTML5) or local database

search.js

(function($){
    "use strict";

    var cache={
        data:{},
        count:0,//Number of data bars
        addData:function(data,key){
            if(!this.data[key]){
                this.data[key]=data;
                this.count++;            
            }
        },
        readData:function(key){
            return this.data[key];
        },
        deleteDataByKey:function(key){
            delete this.data[key];
            this.count--;
        },
        deleteDataByNum:function(num){
            var count=0;
            //No object length Property, only through for in To traverse
            for(var p in this.data){
                if(count>=num) break;
                this.deleteDataByKey(p);
                this.count++;
            }
        }

    };

    function Search(elem,options){
        this.elem=elem;//Already passed in jquery object
        this.options=options;

        this.form=this.elem.find(".search-form");
        this.input=this.elem.find(".search-input");
        this.list=this.elem.find(".search-list");

        this.loaded=false;//Is it loaded? html
        
        //Binding submit event, event agent
        this.elem.on("click",".search-btn",$.proxy(this.submit,this));
        //If autocomplete is set
        if(this.options.autocomplete) this.autocomplete();
    }

    //Default parameters
    Search.defaults={
        autocomplete:false,
        url:"https://suggest.taobao.com/sug?code=utf-8&_ksTS=1484204931352_18291&callback=jsonp18292&k=1&area=c2c&bucketid=6&q=",
        css3:false,
        js:false,
        animation:"fade",
        delay:200//Default 200ms delay
    }

    Search.prototype.submit=function(){
        if($.trim(this.input.val())==="") return false;
        this.form.submit();
    }

    Search.prototype.autocomplete=function(){
        var self=this;
        var timer=null;

        this.input.on("input",function(){

            if(self.options.delay){
                clearTimeout(timer);
                timer=setTimeout(function(){
                    self.getData();
                },self.options.delay);                
            }else{
                self.getData();
                //delay When it is 0, the timer does not need to be turned on
                //Because the timer is asynchronous, even if the delay is 0, it will enter the queued state and cannot be executed immediately
            }

        });

        this.list.showHide(this.options);//towards showhide Component parameters, initialization

        //Show hidden dropdown
        this.input.on("focus",$.proxy(this.showList,this))
                  .on("click",function(){
                      return false;//Prevent bubbling when clicking document
                  });
        $(document).on("click",$.proxy(this.hideList,this));
    }

    Search.prototype.getData=function(){
        var self=this;
        var inputVal=this.input.val();

        if(!inputVal) return self.elem.trigger("search-noData");

        //Determine whether there is a cache
        if(cache.readData(inputVal)) return self.elem.trigger("search-getData",[cache.readData(inputVal)]);

        if(this.jqXHR) this.jqXHR.abort();//Conduct ajax When requesting, terminate the previous request first
        this.jqXHR=$.ajax({
            url:this.options.url+encodeURIComponent($.trim(inputVal)),
            dataType:"jsonp"
        }).done(function(data){
            //Send out data Data, triggering events            
            cache.addData(data,inputVal);//Add cache
            console.log(cache.data);
            console.log(cache.count);
            self.elem.trigger("search-getData",[data]);//Data needs to be in array form
        }).fail(function(){
            //Send failure data, trigger event
            self.elem.trigger("search-noData");
        }).always(function(){
            //After execution
            self.jqXHR=null;
        });    
    }

    Search.prototype.showList=function(){
        //list There's something in it
        if(!this.loaded) return;
        this.list.showHide("show");//Use showhide Component show Method
    }

    Search.prototype.hideList=function(){
        this.list.showHide("hide");//Use showhide Component hide Method
    }

    Search.prototype.setInput=function(val){
        this.input.val(val);
    }

    Search.prototype.appendHTML=function(html){
        this.list.html(html);
        this.loaded=!!html;//!!To Boolean, if html If there is content, it will be true; otherwise, it will be false
    }

    //Plug-in form
    $.fn.extend({
        search:function(opt,value){
            return this.each(function(){
                var ui=$(this);
                var search=ui.data("search");
                //opt Is a parameter object
                var options=$.extend({},Search.defaults,ui.data(),typeof opt==="object"&&opt);
                
                //Single example: one DOM Element corresponds to an instance. If it already exists, it does not need to be instantiated repeatedly
                if(!search){
                    search=new Search(ui,options);
                    ui.data("search",search);
                }
                
                //Exposed methods for external calls
                if(typeof search[opt]==="function"){
                    search[opt](value);
                }
            });
        }
    });

})(jQuery)

Posted by laurajohn89 on Sat, 22 Feb 2020 23:51:28 -0800