前言
日常开发表单,经常会遇到图片上传需求。要求:选择图片上传后端、页面实时渲染缩略图、鼠标悬浮显示删除 / 预览按钮、点击放大预览、删除图片。
本篇使用 jQuery + FormData 实现,代码通俗易懂,适合前端新手。
要点:上传文件必须用
FormData,AJAX 需要关闭processData、contentType。
实现效果
- input file 选择图片,自动上传接口
- 上传成功,页面渲染图片缩略图
- 鼠标悬浮图片,淡出预览、删除图标
- 点击预览:弹窗放大图片
- 点击删除:删掉对应图片,更新视图
- 点击遮罩空白处关闭预览弹窗
HTML + CSS 完整结构
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>图片上传预览删除</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<style>
/* 多图上传外层 */
.more_pic_box {
width: 50%;
display: flex;
flex-direction: column;
}
/* 图片容器 */
.img_box {
width: 100%;
height: 100px;
border: 1px solid gray;
display: flex;
justify-content: start;
align-items: center;
gap: 10px;
}
.img_item {
width: 80px;
height: 80px;
position: relative;
}
.img_item .img_content {
width: 100%;
height: 100%;
}
/* hover显示遮罩 */
.img_item:hover .img_delete_show_box {
opacity: 1;
}
.img_delete_show_box {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
position: absolute;
right: 0;
top: 0;
z-index: 99;
opacity: 0;
transition: all 0.5s;
background-color: rgba(0, 0, 0, 0.5);
gap: 10px;
}
.img_delete,.img_show {
width: 25px;
height: 25px;
cursor: pointer;
}
/* 预览弹窗 */
.preview_modal {
display: none;
position: fixed;
z-index: 999;
top: 0;
left: 0;
width: 100%;
height: 100%;
justify-content: center;
align-items: center;
background-color: rgba(0, 0, 0, 0.3);
}
.previewImage {
width: 30%;
}
</style>
</head>
<body>
<!--图片预览弹窗-->
<div class="preview_modal" onclick="previewCancel()">
<img class="previewImage" id="previewImage" alt="Preview Image">
</div>
<br><br>
<div class="more_pic_box">
<input type="file" class="file_up" id="file_four">
<div class="img_box" id="imgContainer"></div>
</div>
</body>
JS 核心代码
<script>
//后端上传接口地址
let url = 'http://yikatong.kuxia.top'
//保存后端返回图片地址数组
let pics_url_arr = []
$(document).ready(function () {
//页面初始化清空file输入框
$('#file_four').val('')
})
//监听文件选择变化
$('#file_four').change(function () {
pics_upload(this)
})
/**
* 图片上传函数
* @param {DOM} el file dom元素
*/
function pics_upload(el) {
//没有选中文件直接返回
if ($(el)[0].files.length === 0) {
alert('请选择文件')
return
}
//拿到选中文件对象
let file_data = $(el)[0].files[0]
//实例化FormData,专门用来传文件
let pic_data = new FormData()
pic_data.append('file', file_data)
$.ajax({
url: url + '/pc/common/upload',
type: 'POST',
data: pic_data,
dataType: 'json',
cache: false,
processData: false, //文件上传必须关闭,不要处理data为字符串
contentType: false, //不要自动设置请求头
success: function (res) {
if (res.code === 1) {
//上传成功,把图片地址存入数组
pics_url_arr.push(res.data.fullurl)
//渲染图片列表
render_pics(pics_url_arr)
} else {
console.log(res.msg)
}
},
error: function () {
alert('上传失败')
}
})
}
/**
* 渲染图片缩略图
* @param {Array} data 图片url数组
*/
function render_pics(data) {
let str = ''
$('#imgContainer').empty()
if (data && data.length > 0) {
//倒序循环,最新上传图片显示在最左边
for (let i = data.length - 1; i >= 0; i--) {
str += `
<div class="img_item">
<div class="img_delete_show_box">
<img class="img_show" onclick='img_show("${data[i]}")' alt="">
<img class="img_delete" onclick='delete_pic(${i})' alt="">
</div>
<img class='img_content' src="${data[i]}" alt="">
</div>
`
}
$('#imgContainer').html(str)
}
}
/**
* 删除图片
* @param {Number} index 数组下标
*/
function delete_pic(index) {
//根据下标删除数组一项
pics_url_arr.splice(index, 1)
render_pics(pics_url_arr)
$('#file_four').val('')
}
//图片预览弹窗打开
function img_show(img_url) {
$('#previewImage').attr('src', img_url)
$('.preview_modal').css('display', 'flex')
}
//点击遮罩关闭预览,只有点背景才关闭,点图片不关闭
function previewCancel() {
if (event.target.className === 'preview_modal') {
$('.preview_modal').css('display', 'none')
}
}
</script>
核心逻辑大白话讲解
1、FormData 上传文件关键点
上传图片不能直接传对象,必须用FormData。 AJAX 两个配置不能忘:
-
processData:false:不让 jQuery 把数据转成查询字符串 -
contentType:false:不让 jQuery 自动设置请求头,浏览器会自动带上 multipart/form‑datalet pic_data = new FormData()
pic_data.append('file', file_data)
//file就是后端接口接收文件的字段名,后端要和这个名字对应
2、存储图片地址数组 pics_url_arr
后端上传成功返回图片完整 url,push 进数组。整个页面图片全部维护在这个数组。
- 新增图片:push
- 删除图片:
splice(下标,1)删掉数组对应项 - 每次改动数组后调用
render_pics()重新渲染页面,视图和数组保持同步。
这种思路叫数据驱动视图:不去操作 DOM 增删,修改数组,再重新渲染 HTML。
3、倒序渲染
for (let i = data.length - 1; i >= 0; i--)
从数组最后向前循环,最新上传的图片显示在最左侧,符合用户习惯。
4、hover 悬浮遮罩效果
.img_item:hover .img_delete_show_box,鼠标悬浮外层,内部遮罩透明度从 0 变成 1,加transition实现淡入动画。
5、图片预览弹窗
弹窗默认隐藏,点击预览图标赋值图片 src,设置display:flex显示弹窗。
重点:
previewCancel()做判断,只有点击外层遮罩背景才关闭弹窗,点击图片本身不会关闭。
踩坑记录
- 文件上传 AJAX 不写
processData:false、contentType:false,后端接收不到文件。 - input file 选择同一个文件不会触发 change 事件。因为 value 没有变化。
- 删除图片后最好重置
file input:$('#file_four').val('')。 - 后端返回图片路径一定是完整地址,否则图片无法显示。
- onclick 传图片 url,如果路径包含特殊字符容易报错,实际项目可以做转义处理。
总结
整体流程:
- input 选择图片触发 change 事件
- 获取 file 对象,FormData 封装,ajax 上传后端
- 上传成功保存 url 到数组
- 根据数组渲染页面缩略图
- 删除操作修改数组,重新渲染视图
- 预览弹窗实现大图查看