jQuery的使用(三)

这篇博客我们主要想介绍的是 jQuery 跨域、分页以及 jQuery 的插件机制,掌握了上述的三个知识点,我们就能够对 jQuery 的使用以及 MySQL 数据库分页有更好的掌握,这样我们也就有了更多的技能用来解决问题,解决方法多了问题也就简单了,因此还是要努力学习更多的知识,掌握了技能最基本的使用以及原理之后,我们也能去学习更多深入的知识,这样的学习就能形成良好的循环。

1.跨域

这里我们就来介绍关于跨域的一些知识了,主要是想说明跨域是什么,如何解决跨域以及跨域的原理是什么,首先来看看什么是跨域。

1.1 跨域是什么

首先我们要先知道,跨域到底是什么?其实跨域指的就是部署在不同域中的项目需要相互进行访问,那怎样才能算是不同的域呢?就是当协议、域名和端口号这三者中任意一个不相同的时候,就是不同的域了,一般情况下,为了安全,不同域中的资源是不允许相互访问的,这个我们可以进行演示一下。

想要演示跨域的情况的话,我们可以在 Eclipse 配置两个 Tomcat 服务器,比如说一个 Tomcat7,然后另外一个 Tomcat6,如果我们在部署到 Tomcat6 中的项目访问 Tomcat7 中部署项目的资源时,我们就能看到跨域的情况了,这里我们在 Tomcat7 中部署的就是之前使用过的 jqueryAjax 项目,而在 Tomcat6 中部署的则是我们这次新创建的 jqueryCrossDomain 项目。

Eclipse 中配置好 Tomcat6 之后,如果在启动 Tomcat7 之后直接启动 Tomcat6 的话,就会发现会报错,这是因为 Tomcat6Tomcat7 都是默认的配置,所以端口号配置的都是一样的,一起启动的话就会报端口已被占用的错误,因此我们就需要修改 Tomcat6 配置的端口号,修改起来也非常简单,因为我们之前已经在 Eclipse 中配置好了 Tomcat6,如果还不会配置的,可以先看一下我之前关于 Tomcat 服务器的文章,这里我们在 Eclipse 中的 Server 视图是可以看到我们配置的 Tomcat 服务器的,我们需要修改的话就可以双击对应的服务器,然后就能看到一些属性的设置了,我们现在需要修改的是端口号,就可以选择右侧的 Ports 这个选项了,点击之后可以看到有 3 个端口的设定了,默认的值为 800580808009,这里我们直接修改为 900590809009 这三个值就好了,然后一起启动两个 Tomcat 服务器就没问题了。需要注意的一点就是,现在 Tomcat7 中设置的端口号为 8080,而 Tomcat6 中设置的端口号则为 9080

为了看到跨域的情况,我们可以在 jqueryCrossDomain 工程中新建一个 html 文件,然后在其中写上一段 JavaScript 代码。

<script type="text/javascript">
    $.post("http://localhost:8080/jqueryAjax/demo6.html", function() {

    });
</script>

代码的意思就是,当我们使用浏览器打开该 html 文件时,这个页面就会向 jqueryAjax 工程的 demo6.html 页面发送请求,因为 demo6.html 是在另外一个工程中,因此这个请求就是跨域请求了,那页面中会是什么效果呢?其实页面中是没有内容展示的,打开浏览器的控制台,我们就能看到相关提示信息了,也就是浏览器是禁止跨域访问操作的。

1.2 跨域解决方案

在说如何解决跨域访问的限制之前,我们需要先了解一下为什么需要跨域访问,举个例子来进行说明,在我们之前的系统中,可能一个项目会包含多个模块,比如用户管理模块,订单管理模块等,每个模块可能都会有自己的前端页面进行信息的展示,当然有时候一个模块的页面也会需要访问另一个模块,因为都是在同一个项目当中,所以都是没有问题的,但是随着项目规模的逐步扩大,可能就需要将原先的模块抽离出来单独作为一个个的项目,这时候原来一个模块的页面再去访问别的模块就变成了跨域操作,但是这种访问又是必须的,所以我们需要解决浏览器的跨域访问限制。

解决跨域一共有下面 3 种方案,我们下面会详细进行说明。

1.代理方案;
2.jsonp模式;
3.XHR2。

我们先说代理模式,代理模式其实就是不在前端页面中直接访问别的域中的资源,而是像往常一样,还是使用前端页面访问同一项目中的后端代码,比如 Servlet,然后再由后端的 Servlet 去访问别的域中的资源,最后交由前端进行显示,其实这种方式就相当于是将前端跨域转换为了后端跨域。

再看 jsonp 模式,其实是 JSON with Padding 的缩写,即为 json 的一种使用模式,这种方式是可以进行跨域访问的,一般来说,浏览器基于同源策略,是不允许跨域访问的,但是 HTML<script> 元素是一个例外,而 jsonp 模式就是根据 <script> 元素的开放策略来实现跨域的,我们下面也可以进行演示,这种方式当然就是我们要介绍的重点了。

XHR2 这种方式是 XMLHttpRequest Level 2 的缩写,它是由 HTML5 提供的方法,一般用于在移动开发中。

下面我们就简单地演示一下 jsonp 模式这种方式,其实它是基于 <script> 标签的开放策略而来的,因此我们就可以验证一下 <script> 的使用。

需要做的就是,我们先在 jqueryAjax 工程的 WebContent 目录下面新建一个 js 文件夹,然后再到其中新建一个 JavaScript 文件 my.js,里面的内容就为:

alert("I am fine");

然后再到 jqueryCrossDomain 工程中新建一个 HMTL 页面,里面最重要的内容就是这个 script 标签了。

<script type="text/javascript" src="http://localhost:8080/jqueryAjax/js/my.js">

然后我们就可以启动两个项目,然后访问 jqueryCrossDomain 项目中这个新建的页面,这时候我们就可以在浏览器中看到弹出的字符串了,为了做对比,我们还可以使用另外一种方式来访问 jqueryAjax 项目当中的 js 文件,那便是:

<script type="text/javascript">
    $.post("http://localhost:8080/jqueryAjax/js/my.js", function() {

    });
</script>

使用这种方式,我们是看不到字符的打印的,而且打开浏览器的控制台,也会看到相关禁止跨域访问的提示信息,这也就可以证实,使用 <script> 标签是能够实现跨域资源的访问的。

1.3 $.ajax()解决跨域问题

下面我们就来看如何使用 jQuery 解决跨域问题,其实在 jQuery 中有 3 种方式可以解决跨域问题,分别为:

$.ajax()
$.getJSON()
$.getScript()

我们先看如何使用 $.ajax() 来解决跨域问题,我们可以通过改写 jqueryAjax 工程里面的展示商品信息的例子来学习使用跨域,其实修改的重点就在于数据的传输格式要改为 jsonp 格式的,下面先看页面中的主要代码:

<script type="text/javascript">
    $(function() {
        $("#link").toggle(function() {
            var url = "http://localhost:8080/jqueryAjax/productServlet";
            $.ajax({
                url: url,
                type: "get",
                dataType: "jsonp",
                success: function(data) {
                $("#content").html("");
                var json = eval(data);
                var tab=$("<table border='1'><tr><td>编号</td><td>名称</td><td>数量</td><td>价格</td></tr></table>");
                for(var i=0;i<json.length;i++){
                    var obj = json[i];
                    var tr = $("<tr><td>"+obj.id+"</td><td>"+obj.name+"</td><td>"+obj.count+"</td><td>"+obj.price+"</td></tr>");
                    tab.append(tr);
                }
                $("#content").append(tab);
                $("#content").show();
            }
            });
        }, function() {
            $("#content").hide();
        });
    });
</script>
</head>
<body>
    <a href="javascript:void(0)" id="link">显示商品信息</a>
    <div id="content"></div>
</body>

这时候我们点击页面中的显示按钮,就可以在浏览器中看到所发送的请求了,现在发送的请求为:

http://localhost:8080/jqueryAjax/productServlet?callback=jQuery183003895450973838832_1552226608487&_=1552226611490

最明显的区别就是请求中加入了一个 callback 参数,该参数对应的参数值我们先不用管,这就是使用了 jsonp 模式最大的区别了,就是会多一个 callback 参数,但是我们在页面中是没有商品信息展示的,但是上面请求确实是有数据响应回来的,那这是为什么呢?既然已经有数据响应了,为什么没有展示到页面中呢?这里就需要说一下 jsonjsonp 数据的格式区别了:

json格式:[{key:value},{key:value}]
jsonp格式:callback(json)

jsonp 格式的数据则是需要使用请求参数中 callback 参数对应的参数值将 json 数据包起来,然后再进行返回,这样就能够正常显示了,当然这个处理是需要我们在后台代码中处理的,其实也十分简单:

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    // 防止乱码
    response.setCharacterEncoding(StandardCharsets.UTF_8.name());

    String cb = request.getParameter("callback");
    Product product = new Product();
    product.setId(1);
    product.setName("收音机");
    product.setCount(10);
    product.setPrice(20);
    product.setProductDate(new Date());

    Product tvProduct = new Product();
    tvProduct.setId(2);
    tvProduct.setName("电视机");
    tvProduct.setCount(30);
    tvProduct.setPrice(200);
    tvProduct.setProductDate(new Date());

    List<Product> products = new ArrayList<>();
    products.add(product);
    products.add(tvProduct);
    String json = JSONObject.toJSONString(products);
    response.getWriter().write(cb + "(" + json + ")");
}

修改的地方就是先获取得到 callback 参数对应的参数值,然后再使用该参数值将之前的 json 数据包裹起来,最后返回就好了。

当然我们现在会发现请求中 callback 参数对应的参数值似乎太长了,因为这个是随机生成的,其实我们也是可以指定的,那就是使用 jsonpCallback 参数,比如:

$.ajax({
    url: url,
    type: "get",
    dataType: "jsonp",
    jsonpCallback: "cb",
    success: function(data) {
    $("#content").html("");
    var json = eval(data);
    var tab=$("<table border='1'><tr><td>编号</td><td>名称</td><td>数量</td><td>价格</td></tr></table>");
    for(var i=0;i<json.length;i++){
        var obj = json[i];
        var tr = $("<tr><td>"+obj.id+"</td><td>"+obj.name+"</td><td>"+obj.count+"</td><td>"+obj.price+"</td></tr>");
        tab.append(tr);
    }
    $("#content").append(tab);
    $("#content").show();
}
});

这样的话,我们在发送请求时请求中 callback 参数对应的参数值就是 cb 了,确实可以让请求的 url 简洁不少。

1.4 jsonp解决跨域原理

上面我们已经使用 $.ajax() 方法实现了跨域资源的访问,那它实现的原理是什么呢?我们已经知道 $.ajax() 是基于 jsonp 模式实现跨域的,那我们就来看看 jsonp 模式实现跨域的原理是怎样的。

jsonp 模式实现跨域主要是基于 script 标签的开放策略,在上面的内容中我们就知道使用 script 是可以访问跨域资源的,那我们在这里就可以写一个方法,就是用于创建 script 标签元素的,然后为新创建的标签元素设定各种属性,例如 typesrc 属性,当然为了灵活,这个 src 属性我们可以设置为传入的,然后再将该创建的元素标签添加到页面中,这个新建的标签元素就可以执行了,也就可以访问得到跨域的资源了,这里还有需要说明的一点就是,因为跨域请求的 url 中都会有 callback 参数,因此我们需要设定一个与该 callback 参数对应参数值名称相同的回调函数,这样我们就可以在回调函数中处理响应回来的数据了。完整的代码如下:

<script type="text/javascript">
    // 原理就是我们使用js代码在页面中添加一个<script>标签
    // 定义函数,作用为添加script标签
    function addScript(src){
        // 创建一个script标签
        var script = document.createElement("script");
        // 设置script标签属性
        script.setAttribute("type","text/javascript");
        script.src = src;
        // 将script标签添加到页面中
        document.body.appendChild(script);
    }

    $(function(){
        addScript("http://localhost:8080/jqueryAjax/productServlet?callback=cb")
    });

    // 设置回调函数
    function cb(data){
        var jsonObj = eval(data);
        alert(jsonObj[0].name)
    }
</script>

这就是 jsonp 模式实现跨域的原理了,当然上面的代码只是该原理的最简单实现了,jQuery 提供的几个方法实现中代码肯定是更加完善的,但是就是因为上面的代码简单,所以我们就可以通过它更容易地掌握相应的原理。

1.5 $.getJSON()解决跨域问题

我们下面再来介绍使用 $.getJSON() 方法来解决跨域问题,其实使用 $.getJSON() 方法就相当于是使用 $.ajax() 解决跨域问题的简化,因此使用起来是更加简单的,下面先看代码:

var url = "http://localhost:8080/jqueryAjax/productServlet?callback=?";
$.getJSON(url, function(data) {
    $("#content").html("");
    var json = eval(data);
    var tab=$("<table border='1'><tr><td>编号</td><td>名称</td><td>数量</td><td>价格</td></tr></table>");
    for(var i=0;i<json.length;i++){
        var obj = json[i];
        var tr = $("<tr><td>"+obj.id+"</td><td>"+obj.name+"</td><td>"+obj.count+"</td><td>"+obj.price+"</td></tr>");
        tab.append(tr);
    }
    $("#content").append(tab);
    $("#content").show();
});

是不是很简单,就跟我们之前使用 $.post() 方法是一样的,不过有一点需要特别注意的就是,请求的 url 中一定要带 callback 参数,而且该参数的参数值一定要设置为 ?,在真正发送请求时,这个 ? 号是会被替换的,我们可以看一下真实发送请求的 url

http://localhost:8080/jqueryAjax/productServlet?callback=jQuery18307803852774240552_1552230739083&_=1552230742301

1.6 $.getScript()解决跨域问题

jQuery 中还有最后一个用于解决跨域问题的方法没有介绍了,那就是 $.getScript() 了,该方法主要是用于载入跨域的 js 文件,我们可以使用该方法来替换使用 script 标签访问跨域的 js 文件的,使用 script 标签时,代码应该为:

<script type="text/javascript" src="http://localhost:8080/jqueryAjax/js/my.js">

在使用 $.getScript() 方法的时候,就可以这样使用了。

$.getScript("http://localhost:8080/jqueryAjax/js/my.js",function(){

});

2.分页

下面我们开始介绍一个使用非常频繁的技术,那就是分页了,当我们使用搜索引擎时,输入一个关键字,可能会有很多条信息返回,这时候页面中的显示就是分页显示的,因为一页全部显示的话,数据量太大可能会导致显示非常不友好,而采用分页的话,则会使页面显示更加友好,查找需要的消息也更加好定位。

2.1 分页的分类

分页的实现的话,可以分为两类:

1.物理分页
2.逻辑分页

物理分页是一次查询一个页面容量的数据进行显示,比如一页显示 10 条数据,那我们每次查询数据库时就会查对应页面的 10 条记录,当然不同的数据库实现起来会不一样,MySQL 数据库是使用 limit 关键字实现的,而 Oracle 则是使用 rownum 关键字来实现的;逻辑分页则是一次将数据全部查询出来,然后使用游标查看对应页面的数据,Java 中对应的接口则是 ResultSet,即查询数据库所得的结果集。

2.2 分页的原理

要想了解分页的原理,我们可以从前端发送请求,然后后台接受请求并进行处理这个流程来说,首先,要想分页显示数据的话,我们需要在前端将当前页码和每页显示条数传到后台,也就是告诉后端应该返回哪一页的数据,而后端在处理请求之后,则应该将当前页的数据返回,而除了当前页的数据之外,在前端进行展示时,我们还需要知道总页数,这样的话我们就还需要查出总的记录条数,然后算出总页数来,这就是进行分页展示请求的整个流程了。

而在这个请求的过程中,我们需要一些必备的要素,比如当前页数,每页显示条数,总记录条数,总页数以及当前页的数据。

MySQL 数据库中,我们分页则是使用的 limit 关键字,使用样式则是:

select * from 表名 limit num1,num2

num1 表示的则是从第几行开始查询,默认是从 0 行开始的,而 num2 则是表示要查询多少条数据,因此计算 num1 时,我们则可以使用 (页码-1)*每页条数 进行计算,因此可以抽象为以下公式:

select * from 表名 limit (页码-1)*每页条数,每页显示条数

3.jQuery插件机制

jQuery 插件其实就是对 jQuery 功能的增强,让其中的 jQuery 对象拥有更多的方法,我们在介绍 jQuery 插件之前,首先先介绍一下 serialize()serializeArray() 方法。

3.1 serialize()和serializeArray()方法

首先来看一下这两个方法的作用:

serialize():将表单元素中的内容序列化为字符串
serializeArray():将表单元素中的内容序列化为json数据

在平时的开发中,当页面中有一个表单时,我们需要异步提交这个表单里面的数据时,应该怎么办呢?最直接的办法可能是获取到每个输入框中输入的值,然后再拼接到请求体中进行发送,但是当表单中元素比较多时,这样操作就会十分麻烦了,那有没有什么简便的方法呢?是有的,那就是 serialize() 方法了,首先先假设页面中有如下表单:

<form action="">
    用户名:<input type="text" name="userName" value="kobe"/><br>
    密码:<input type="password" name="password" value="lakers"/>
</form>

那我们就可以使用如下代码获取表单中元素输入的值了:

$(function(){
    var key = $("form").serialize();
    alert(key);
});

得到的结果就是 userName=kobe&password=lakers 了,也是我们在发送请求时所需要的格式了,所以就可以满足我们的要求了,那使用 serializeArray() 方法又会得到什么结果呢?可以使用如下的代码:

$(function(){
    var key = $("form").serializeArray();
    console.info(key);
});

得到的结果如下:

[{name: "userName", value: "kobe"},{name: "password", value: "lakers"}]

很明显这种 json 格式不是我们所需要的,我们所需要的 json 格式应该是这样的:

{"userName": "kobe","password": "lakers"}

所以我们就需要将现在得到格式的 json 数据转换为我们需要格式的,这个我们在下面进行介绍。

3.2 插件机制

下面开始介绍 jQuery 的插件机制,jQuery 的插件机制有两种模式,一种是对 jQuery 对象本身的扩展,另一种则是对 jQuery 选中数据集对象的扩展,分别如下:

jQuery.extend(object)
jQuery.fn.extend(object)

说明一下,使用 jQuery.extend(object) 方法进行的扩展,则是对 jQuery 对象本身的扩展,扩展之后的使用方式则是和 $.post() 方法使用类似的;而使用 jQuery.fn.extend(object) 方法进行的扩展,则是对 jQuery 选中数据集对象的扩展,扩展之后的使用方式则是和 $("#form").serialize() 方法使用类似的。

下面我们使用具体的例子来对这两种扩展方式进行说明,首先看 jQuery.extend(object) 方式的:

$.extend({
    maxValue:function(a,b){
        return a>b?a:b;
    },
    minValue:function(a,b){
        return a>b?b:a;
    }
});

$(function(){
    alert($.maxValue(10,20));
    alert($.minValue(10,20));
});

上面的代码就相当于是为 jQuery 对象本身扩展了两个数中求较大值和较小值的方法了,而在下面的代码也可以看出调用扩展的方法也是十分简单的。

现在再看使用 jQuery.fn.extend(object) 方式进行扩展的,我们就扩展将表单元素中的内容序列号为 json 数据,之前使用 serializeArray() 方法得到的虽然也是 json 格式的数据,但是它的格式并不是我们想要的,因此我们需要扩展一个方法,将目前使用 serializeArray() 方法得到的 json 数据转换成我们需要的:

$.fn.extend({
    serializeJson:function(){
        var json = {};
        var msg = this.serializeArray();
        $(msg).each(function(){
            json[this.name] = this.value;
        });
        return json;
    }
});

这样的话,就得到我们所需要格式的数据了。

{userName: "kobe", password: "lakers"}

但是需要注意的一点就是,目前表单元素中只有两个输入框,当有别的表单元素,比如多选框时,上面的代码就不能奏效了,因此我们还需要优化上面的代码,首先表单元素可能为:

<form action="">
    用户名:<input type="text" name="userName" value="kobe"/><br>
    密码:<input type="password" name="password" value="lakers"/><br>
    爱好:<input type="checkbox" name="hobby" value="eat" checked="checked">吃
        <input type="checkbox" name="hobby" value="drink" checked="checked">喝
        <input type="checkbox" name="hobby" value="play" checked="checked">玩
</form>

如果还是使用上面的代码的话,我们得到的结果为:

{userName: "kobe", password: "lakers", hobby: "play"}

这样的肯定就是不符合要求的了,因为爱好那三个选择框是都选择了的,但是最后获取到的值却只有最后一个,显然是不对的,我们想要的格式应该是这样的:

{userName: "kobe", password: "lakers", hobby: ["eat", "drink", "play"]}

那我们就应该优化之前的代码了,优化后的代码如下:

$.fn.extend({
    serializeJson:function(){
        var json = {};
        var msg = this.serializeArray();
        $(msg).each(function(){
            if(json[this.name]){
                // json中对应的键已经存在
                if(json[this.name].push){
                    // json中相关键对应的值为数组
                    json[this.name].push(this.value);
                }else{
                    // json中相关键对应的值不是数组
                    json[this.name] = [json[this.name]];
                    json[this.name].push(this.value);
                }
            }else{
                // json中对应的键不存在
                json[this.name] = this.value;
            }
        });
        return json;
    }
});

这样我们就能得到我们所期望格式的数据了,以后我们再想获得表单元素序列化后的 json 数据,就可以直接使用这个 serializeJson() 方法了,使用方法为:

var json = $("form").serializeJson();

4.总结

关于 jQuery 的知识在这里就总结完了,知识有很多,学习的时候重点还是在于多使用,用的多了自然也就熟悉了,其实写博客整理知识的目的也十分简单,就是当自己需要用到某些知识点,而又记得不是很清楚时,可以随时回来进行查询,整理一遍的话自己的印象也会更加深刻,因此还是应该坚持下去,越坚持越有力量。

坚持原创技术分享,您的支持将鼓励我继续创作!