如何统计动态追加的图片元素并基于数量应用自定义样式?
我正在制作一个本地静态页面,包含图片上传器和预览画廊,用户选择图片后会将图片追加到<div class="preview__gallery"></div>中,上传器使用的是<input type="file" multiple="multiple">。
问题在于,图片追加到画廊后,我想通过jQuery的.length属性统计图片数量,再根据统计结果用.css()应用自定义样式,但类选择器无法定位这些图片。我尝试过$.when().done()延迟函数、MutationObserver监听DOM变化、事件委托等方法都没成功——因为不需要触发额外事件,只需要在图片追加完成后执行统计逻辑。
目前我在追加图片的函数后调用统计函数作为回调,第一个函数内打印console.log('before'),统计函数内打印console.log('after'),回调看似正常执行,但.length属性无效,console.log($('.preview__thumbs'))输出的是空jQuery对象。
现有代码
HTML部分
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <div class="concorso__body"> <div class="container"> <div class="row"> <div class="col-lg-8"> <div class="single__upload"> <div class="upload__field"> <input type="file" accept="image/*" multiple="multiple" class="drag__area" id="file__input"> <div class="upload__text"> <i class="bi bi-plus"></i> <span>Trascina o clicca per caricare immagini</span> </div> </div> <div class="preview__gallery" id="the__gallery"></div> </div> </div> <div class="col-lg-4"></div> </div> </div> </div>
JS部分
$(document).ready(function() { $(function() { function imagesPreview(input, placeToInsertImagePreview, callback) { if (input.files) { var filesAmount = input.files.length; for (i = 0; i < filesAmount; i++) { var reader = new FileReader(); reader.onload = function(event) { $($.parseHTML('<img class="preview__thumbs">')).attr('src', event.target.result).appendTo(placeToInsertImagePreview); }; reader.readAsDataURL(input.files[i]); console.log('before'); } if (typeof callback === 'function') { callback(); } } } }); });
问题原因
核心问题是FileReader的onload是异步事件:循环中调用reader.readAsDataURL()后,会立刻执行后续的回调函数,但此时图片还未加载完成、也未插入到DOM中,导致回调执行时统计不到元素。循环内的console.log('before')会在onload触发前就执行,造成回调跑在图片插入DOM之前的假象。
另外你的代码还遗漏了文件输入框的change事件绑定,导致imagesPreview函数根本没有被调用。
解决方案
方案1:跟踪加载完成数量,全部加载后执行回调
通过计数跟踪每个图片的加载状态,等所有图片都插入DOM后再执行统计回调:
$(document).ready(function() { function imagesPreview(input, placeToInsertImagePreview, callback) { if (input.files) { var filesAmount = input.files.length; var loadedCount = 0; // 跟踪已加载完成的图片数量 for (let i = 0; i < filesAmount; i++) { var reader = new FileReader(); reader.onload = function(event) { $($.parseHTML('<img class="preview__thumbs">')).attr('src', event.target.result).appendTo(placeToInsertImagePreview); loadedCount++; // 所有图片加载并插入DOM后执行回调 if (loadedCount === filesAmount && typeof callback === 'function') { callback(); } }; reader.readAsDataURL(input.files[i]); } } } // 绑定文件选择事件 $('#file__input').on('change', function() { imagesPreview(this, '#the__gallery', function() { // 统计图片数量并应用样式 var imgCount = $('.preview__thumbs').length; console.log('图片数量:', imgCount); // 示例:根据数量设置画廊样式 if (imgCount > 0) { $('#the__gallery').css('padding', '10px'); } }); }); });
方案2:用Promise封装异步操作,批量处理
用Promise封装FileReader的读取过程,等待所有图片读取完成后再批量插入DOM并执行回调:
$(document).ready(function() { // 封装FileReader为Promise function readFile(file) { return new Promise(resolve => { var reader = new FileReader(); reader.onload = function(event) { resolve(event.target.result); }; reader.readAsDataURL(file); }); } async function imagesPreview(input, placeToInsertImagePreview, callback) { if (input.files) { var files = Array.from(input.files); // 等待所有文件读取完成 var imageSrcList = await Promise.all(files.map(readFile)); // 批量插入图片到DOM imageSrcList.forEach(src => { $($.parseHTML('<img class="preview__thumbs">')).attr('src', src).appendTo(placeToInsertImagePreview); }); // 所有图片插入后执行回调 if (typeof callback === 'function') { callback(); } } } // 绑定文件选择事件 $('#file__input').on('change', function() { imagesPreview(this, '#the__gallery', function() { var imgCount = $('.preview__thumbs').length; console.log('图片数量:', imgCount); // 自定义样式逻辑 }); }); });
内容的提问来源于stack exchange,提问作者Richard

