JavaWeb 学习笔记 8:AJAX

news2024/11/18 15:46:22

JavaWeb 学习笔记 8:AJAX

AJAX(Asynchronous JavaScript And XML,异步 js 和 XML)是一种用 js 代码异步(或同步)的方式请求服务端数据,并在页面显示或加载的技术。

1.快速入门

先看如何用纯 js 的方式使用 AJAX:

定义一个用于响应 AJAX 请求的 Servlet:

@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().println("Hello World!");
    }
}

定义一个 html 页面:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<button onclick="doAjaxRequest()">send</button>
</body>
<script>
    function doAjaxRequest(){
        // 创建 XMLHttpRequest 对象
        const xhttp = new XMLHttpRequest();

        // 定义回调函数
        xhttp.onreadystatechange = function() {
            if (this.readyState == 4 && this.status == 200) {
                let result = this.responseText;
                console.log(result);
            }
        };
        // 发送请求
        xhttp.open("GET", "http://localhost:8080/ajax-demo/hello", true);
        xhttp.send();
    }
</script>
</html>

js 方法doAjaxRequest负责创建一个 XMLHttpRequest 对象,并用其发送 AJAX 请求,然后将返回的内容打印在控制台中。

关于原生 AJAX 的详细说明,可以阅读这里。

点击按钮,可以看到浏览器发送了一个类型为 XHR(XMLHttpRequest) 的请求到服务端:

image-20230913114146316

并且可以看到控制台输出。

2.案例:验证用户是否存在

服务端:

@WebServlet("/user/exist")
public class ExistController extends HttpServlet {
    private UserService userService = new UserServiceImpl();
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String username = req.getParameter("username");
        boolean exists = userService.checkUsernameExists(username);
        if (exists){
            resp.getWriter().print("true");
        }
        else{
            resp.getWriter().print("false");
        }
    }
}

在页面表单中输入用户名的元素上设置光标焦点事件:

<input name="username" type="text" id="username" onblur="checkUsernameExists()">

对应的 js:

// 检查用户名是否存在
function checkUsernameExists() {
    let username = $("input#username").val();
    const xhttp = new XMLHttpRequest();
    xhttp.onreadystatechange = function() {
        if (this.readyState == 4 && this.status == 200) {
            let result = this.responseText;
            console.log(result)
            if (result == "true"){
                console.log("用户名已存在")
                $("span#username_err").html("用户名已存在");
                $("span#username_err").show();
            }
            else{
                $("span#username_err").hide();
            }
        }
    };
    xhttp.open("POST", "http://localhost:8080/login-demo/user/exist?username="+username, true);
    xhttp.send();
}

3.Axios

Axios 是封装好的一个异步调用框架,可以运行于浏览器或 Node.js 服务器上。

使用 Axios 可以让我们之前的 AJAX 调用的代码更为简单。

在 html 中添加对 Axios 的引用:

<head>
    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>

修改 AJAX 部分代码,使用 Axios 进行调用:

// 检查用户名是否存在
function checkUsernameExists() {
    let username = $("input#username").val();
    axios({
        method: 'post',
        url: '/login-demo/user/exist?username=' + username
    })
        .then(function (response) {
        let result = response.data;
        if (result) {
            $("span#username_err").html("用户名已存在");
            $("span#username_err").show();
        } else {
            $("span#username_err").hide();
        }
    });
}

axios对象可以接收一个 js 对象,其method属性指定了 HTTP Method,url指定请求的 url。

需要注意的是,这里 Axios 将返回的响应报文体进行了解析,所以这里response.data不是一个字符串形式的truefalse,而是 bool 类型。

除了上边的方式,还可以使用一种简化的方式:

// 检查用户名是否存在
function checkUsernameExists() {
    let username = $("input#username").val();
    axios.post('/login-demo/user/exist?username=' + username)
        .then(function (response) {
        let result = response.data;
        if (result) {
            $("span#username_err").html("用户名已存在");
            $("span#username_err").show();
        } else {
            $("span#username_err").hide();
        }
    });
}

4.JSON

通常浏览器和服务端通过异步调用方式传输的数据结构都很复杂,所以会使用 JSON 格式的字符串进行传输。

浏览器端:

// 检查用户名是否存在
function checkUsernameExists() {
    let username = $("input#username").val();
    axios.post('/login-demo/user/exist', {"username": username})
        .then(function (response) {
        let result = response.data;
        if (result.exist) {
            $("span#username_err").html("用户名已存在");
            $("span#username_err").show();
        } else {
            $("span#username_err").hide();
        }
    });
}

服务端:

@WebServlet("/user/exist")
public class ExistController extends HttpServlet {
    private UserService userService = new UserServiceImpl();
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        BufferedReader reader = req.getReader();
        StringBuilder sb = new StringBuilder();
        do{
            String line = reader.readLine();
            if (line == null){
                break;
            }
            sb.append(line);
        }
        while (true);
        String content = sb.toString();
        System.out.println(content);
        JSONObject jsonObject = JSON.parseObject(content);
        String username = (String) jsonObject.get("username");
        boolean exists = userService.checkUsernameExists(username);
        JSONObject resultJO = new JSONObject();
        if (exists){
            resultJO.put("exist", true);
        }
        else{
            resultJO.put("exist", false);
        }
        resp.setContentType("application/json;charset=UTF-8");
        resp.getWriter().print(resultJO.toJSONString());
    }
}

这里使用了一个中间件 FastJSON,用于在服务端解析和编码 JSON 字符串:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.75</version>
</dependency>

5.案例:异步加载品牌列表

可以用 Axios+JSON 异步加载品牌列表。

后端:

@WebServlet("/brand/list")
public class ListController extends HttpServlet {
	// ...
    /**
     * 获取品牌列表
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/json;charset=utf-8");
        List<Brand> brands = brandService.getAllBrands();
        response.getWriter().print(JSON.toJSONString(brands));
    }
}

前端:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript" src="/login-demo/js/jquery-3.7.1.min.js"></script>
    <script type="text/javascript" src="/login-demo/js/axios-0.18.0.js"></script>
</head>
<body>
<h1>${username},欢迎您</h1>
<a href="/login-demo/brand/add"><input type="button" value="新增"></a><br>
<hr>
<table id="brandsTable" border="1" cellspacing="0" width="800">
    <tr>
        <th>序号</th>
        <th>品牌名称</th>
        <th>企业名称</th>
        <th>排序</th>
        <th>品牌介绍</th>
        <th>状态</th>
        <th>操作</th>
    </tr>
    <tr align="center" class="brandsRow" hidden="hidden">
        <td></td>
        <td></td>
        <td></td>
        <td></td>
        <td></td>
        <td></td>
        <td><a href="/login-demo/brand/edit?id=#" class="edit">修改</a>
            <a href="/login-demo/brand/delete?id=#" class="delete">删除</a></td>
    </tr>
</table>
</body>
<script type="text/javascript">
    $(document).ready(function () {
        loadBrandsTable();
    });

    function loadBrandsTable() {
        let table = $("table#brandsTable");
        let demoRow = table.find("tr.brandsRow:first");
        // 获取品牌列表信息
        axios({
            "method": "POST",
            "url": "/login-demo/brand/list"
        })
        .then((resp)=>{
            let brands = resp.data;
            brands.map((brand)=>{
                let newRow = demoRow.clone();
                console.log(newRow);
                fillRowData(newRow, brand);
                table.find("tr:last").after(newRow);
            });
        });
    }

    function fillRowData(newRow, data){
        newRow.children("td:eq(0)").html(data.id);
        newRow.children("td:eq(1)").html(data.brandName);
        newRow.children("td:eq(2)").html(data.companyName);
        newRow.children("td:eq(3)").html(data.ordered);
        newRow.children("td:eq(4)").html(data.description);
        let status = "禁用";
        if (data.status == 0){
            status = "启用";
        }
        newRow.children("td:eq(5)").html(status);
        newRow.children("td:eq(6)").children("a.edit").attr("href","/login-demo/brand/edit?id="+data.id);
        newRow.children("td:eq(6)").children("a.delete").attr("href","/login-demo/brand/delete?id="+data.id);
        newRow.removeAttr("hidden");
    }
</script>
</html>

这里为表格添加了一个用于示例数据的行,并使用 JQuery 选择器获取这个行的 DOM 对象,并进行拷贝,填充数据后依次添加到表格的末尾。

用类似的方式可以将新增品牌也修改为前端异步提交而非表单提交,这里不再赘述。

6.Template

在前边的示例中,使用一个表单中的“隐藏行”作为模板,用于遍历品牌信息并填充数据,然后添加到表格 DOM 树中,并最终称为一个有数据的表格。

这样做是可行的,但是“隐藏行”只是用户看不见,实际上依然是存在的,并且会影响页面的渲染速度。对此,有一个专门用于此类问题的 Html 标签 template,可以用它作为模板 Html 代码存放的位置。这样做的好处在于,<template>标签中的内容是一个 DocumentFragment(文档片段),它不是当前Document(文档)的一部分,不是任何DOM对象的子节点,所以它不会参与页面渲染,就不影响性能。但同时我们依然可以通过 js 代码获取其内部 DOM 结构,作为模板进行使用。

使用 template 标签改写之前的示例:

<table id="brandsTable" border="1" cellspacing="0" width="800">
    <tr>
        <th>序号</th>
        <th>品牌名称</th>
        <th>企业名称</th>
        <th>排序</th>
        <th>品牌介绍</th>
        <th>状态</th>
        <th>操作</th>
    </tr>
</table>
<template id="rowTemplate">
    <tr align="center">
        <td></td>
        <td></td>
        <td></td>
        <td></td>
        <td></td>
        <td></td>
        <td><a href="/login-demo/brand/edit?id=#" class="edit">修改</a>
            <a href="/login-demo/brand/delete?id=#" class="delete">删除</a></td>
    </tr>
</template>

对应的 js 代码:

function loadBrandsTable() {
    let table = $("table#brandsTable");
    let demoRow = $("#rowTemplate").get(0).content.querySelector("tr");
    // 获取品牌列表信息
    axios({
        "method": "POST",
        "url": "/login-demo/brand/list"
    })
        .then((resp)=>{
        let brands = resp.data;
        brands.map((brand)=>{
            let newRow = $(demoRow).clone();
            // console.log(newRow);
            fillRowData(newRow, brand);
            table.find("tr:last").after(newRow);
        });
    });
}

需要注意的是,template 标签的内容是文档片段,而文档片段并不是当前文档的一部分。如果用开发者工具观察文档结构:

image-20230915104618250

template 标签本身是当前文档的一部分,是可以通过文档操作的方式获取的,比如用 JQuery:$("#rowTemplate").get(0)。但是,其内部的内容是一个文档片段(#document-fragment标记的部分),这部分内容并不属于当前文档,所以你是没办法用类似$("#rowTemplate").children()的方式获取到的,这个筛选项的长度只能是0,没有任何内容。

除了上边示例中使用templatecontent属性获取 DOM 对象外,还可以用 JQuery 的方式:

let demoRow = $($("#rowTemplate").html()).get(0);

这里是先获取template标签内的 html 内容,然后用 JQuery 解析以生成 DOM 对象。

两者效果是相同的,只是写法不同。

本文的完整示例可以从这里获取。

7.参考资料

  • 黑马程序员JavaWeb基础教程
  • AJAX 简介 (w3school.com.cn)
  • jQuery 教程 (w3school.com.cn)
  • DocumentFragment - Web API 接口参考 | MDN (mozilla.org)
  • DOMParser - Web API 接口参考 | MDN (mozilla.org)
  • javascript中html字符串转化为jquery dom对象的方法

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1035994.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

GB28181协议-注册详解

注册指的是设备或系统进入联网系统时向SIP服务器&#xff08;SIP UAS&#xff09;进行注册登记的工作模式&#xff0c;在本文中FFmpeg即为一个SIP服务器&#xff0c;设备向FFmpeg发送注册请求&#xff0c;FFmpeg在接收到设备的注册请求后返回相应的回复消息&#xff0c;则完成设…

【面试题】——Spring

1.Spring是什么&#xff1f; Spring是一个开源的Java应用框架&#xff0c;它提供了广泛的基础设施支持&#xff0c;用于构建Java应用程序。极大提高了开发效率。它提供了一种轻量级的编程模型&#xff0c;通过依赖注入&#xff08;Dependency Injection&#xff09;和面向切面…

盘点神级AI绘画工具,太强太友好了

B站|公众号&#xff1a;啥都会一点的研究生 AI绘画越来越卷、越来越极致&#xff0c;可以轻松将我们这些毫无绘画功底的人的创意想法转化为令人惊叹的数字艺术作品&#xff0c;本期将整理一些目前流行的高质量AI艺术生成器&#xff0c;一起看看吧 Bing Image Creator https:/…

污水中常规五参数的检测有什么意义

便携式水质五参数检测仪包括&#xff1a; 温度、 pH、溶解氧、电导率、浊度。同时该仪器也可根据客户需要进行其他电极法指标&#xff0c; 如盐度、 TD S、OR P 指标的增添&#xff0c;以及指标的任意搭配组合。 水质常规五参数在我国水污染防治中发挥着重要的作用。 检测水质常…

spring中AbstractApplicationContext的refresh()

这个部分的源码看的次数不计其数了&#xff0c;每次看不同开源组件的调用过程中走到这里都有不同的收获。 spring 中 AbstractApplicationContext 的 refresh() 是 spring 的核心&#xff0c;几乎所有的逻辑都在是这里间接被调用。 如下源码为 spring boot 2.7.15 自带的 sprin…

MySQL主从复制与读写分离(附配置实例)

目录 一、主从复制1.1 什么是主从复制&#xff1f;1.2 MySQL支持的复制类型1.3 主从复制的工作过程1.4 主从复制的同步模式1.4.1 异步复制&#xff08;Asynchronous replication&#xff09;1.4.2 全同步复制&#xff08;Fully synchronous replication&#xff09;1.4.3 半同步…

其他网页都可以打开,只有知网打不开

问题&#xff1a;电脑自动获取DNS出现问题。 1、打开Internet协议版本4&#xff08;TCP/IPv4&#xff09; 控制面板—所有控制面板项----网络连接----WLAN—属性----Internet协议版本4&#xff08;TCP/IPv4&#xff09;—属性 2、修改DNS 将自动获得DNS服务器地址&#xff…

Sparta工具用法描述之信息收集(漏洞分析)

声明:本文仅做学习与交流,任何用于非法用途、行为等造成他人损失的,责任自负。本文不承担任何法律责任。 Sparta是python GUI应用程序,它通过在扫描和枚举阶段协助渗透测试仪来简化网络基础结构渗透测试。 通过点击并单击工具箱并以方便的方式显示所有工具输出,它可以使测…

Swing通过后台线程实现页面更新

业务场景 在swing程序中&#xff0c;我们想实现一个随着任务的执行而同步更新组件的功能&#xff0c;如果在主线程&#xff08;EDT&#xff09;中直接执行任务并更新组件&#xff0c;则会导致组件只显示最终更新的状态的问题&#xff0c;这是因为EDT是单线程的&#xff0c;在执…

【golang】深入理解Go语言垃圾回收(GC)

垃圾回收 垃圾回收版本1.3之前标记-清除&#xff08;mark and sweep&#xff09;算法标记-清除&#xff08;mark and sweep&#xff09;的缺点 版本1.5的三色并发标记法没有STW的三色标记法屏障机制强-弱 三色不等式插入屏障删除屏障 版本1.8的混合写屏障&#xff08;hybrid wr…

MQ - 25 RabbitMQ集群架构设计与实现

文章目录 导图集群构建节点发现元数据存储数据可靠性镜像队列仲裁队列安全控制传输加密身份认证资源鉴权可观测性总结导图 集群构建 集群构建由节点发现和元数据存储两部分组成。RabbitMQ 也是一样的实现思路。 节点发现 在节点发现方面,RabbitMQ 通过插件化的方式支持了多…

经典题记录 字符串相加/相乘

1. LeetCode 415 字符串相加 代码一&#xff1a;代码简短&#xff0c;但需要借助额外的一个string来保存结果&#xff0c;更占用内存。 class Solution { public:string addStrings(string num1, string num2) {string ans"";int size1num1.size();int size2num2.si…

php之导入导出csv文件

一、导入csv文件 1、创建导入页面 <!DOCTYPE html> <html lang"zh-CN"> <meta charset"UTF-8"> <head><title>文件提交表单</title> </head> <body> <form action"test5.php" method"…

嵌入式裸机架构的探索与崩塌

为什么会想着探索下嵌入式裸机的架构呢&#xff1f;是因为最近写了一个项目&#xff0c;项目开发接近尾声时&#xff0c;发现了一些问题&#xff1a; 1、项目中&#xff0c;驱动层和应用层掺杂在一起&#xff0c;虽然大部分是应用层调用驱动层&#xff0c;但是也存在驱动层调用…

6年Android开发前10月的总结,写给正在求职的安卓开发

进入大厂工作对许多人来说已经是一种挑战&#xff0c;但只要充分准备&#xff0c;很多问题都可以逐步解决。当然&#xff0c;运气也起到了一定的作用&#xff0c;但最终还是与自身的努力密不可分。运气是实力的一部分&#xff0c;因为自助者天助。 每到10月进行总结时&#xff…

华为ICT——第二章-数字图像处理私人笔记

目录 1&#xff1a;计算机视觉&#xff1a;​编辑 2&#xff1a;计算机视觉应用&#xff1a;​编辑 3&#xff1a;计算机视界核心问题&#xff1a;​编辑 4&#xff1a;相关学科&#xff1a; 5&#xff1a;计算机视觉与人工智能&#xff1a; 最成熟的技术方向是图像识别 6…

Django应用及分布式路由

Django应用及分布式路由 应用 应用在Django项目中一个完全独立的业务模块&#xff0c;可以包含自己的路由&#xff0c;视图&#xff0c;模板&#xff0c;模型 应用配置 在这里面添加你自定义的应用 INSTALLED_APPS [django.contrib.admin,django.contrib.auth,django.cont…

如何使用docker快速部署MinDoc文档系统

MinDoc是非常优秀的知识分享系统&#xff0c;但是很多刚接触的人会一脸懵逼&#xff0c;而且官方文档写的也并不清晰&#xff0c;所以和大家分享一下快速部署MinDoc的方法。 首先docker环境先自行安装好&#xff0c;这里不再赘述。 拉取docker镜像&#xff1a; docker pull …

解决 Github port 443 : Timed out

解决方法 打开代理页面 打开 设置 --> 网络与Internet --> 查找代理 记录下当前系统代理的 IP 地址和端口号 如上图所示&#xff0c;地址与端口号为&#xff1a;127.0.0.1:7890 注意修改成自己的IP和端口号 git config --global http.proxy http://127.0.0.1:7890 gi…

面试总结之微服务篇

一、概览 1、微服务常用组件 微服务给系统开发带来了诸多问题和挑战&#xff0c;如服务间通信和调用的复杂性、数据一致性和事务管理、服务治理和版本管理等&#xff0c;为解决应对这些问题和挑战&#xff0c;各种微服务组件应运而生微服务的常见组件和实现&#xff1a; 1…