jQuery file upload cropper的 click .preview事件没有绑定成功
测试
修改https://github.com/tkvw/jQuery-File-Upload/blob/master/basic-plus.html
var node = $('<p id="chuck" class="preview"/>')
.append($('<span/>').text(file.name));
给p增加id和class,在页面加载后,
打开浏览器的console,然后手动执行,然后单击图片。会发现这个事件是会触发的
$(".preview").on("click", function(){
alert("test");
})
但是jquery.fileupload-image-editor.js中的没有触发,事件应该是绑定了
手动移除事件
$(".preview").off("click"); https://stackoverflow.com/questions/209029/best-way-to-remove-an-event-handler-in-jquery
分析
1.jquery.fileupload-image-editor.js定义了uploadImageEditorPreviewSelector
uploadImageEditorPreviewSelector: 'click .preview',
2,.jquery.fileupload-ui.js中定义了filesContainer
// The container for the list of files. If undefined, it is set to
// an element with class "files" inside of the widget element:
filesContainer: undefined,
_initEventHandlers: function () {
this._super();
var handlers = {};
handlers[this.options.uploadImageEditorPreviewSelector] = this._previewHandler.bind(this);
this._on(this.options.filesContainer, handlers);
},
文件上传之后
jquery.fileupload.js中_onAdd: function (e, data) {
that._initResponseObject(newData);
that._initProgressObject(newData);
that._addConvenienceMethods(e, newData);
result = that._trigger(
'add',
$.Event('add', {delegatedEvent: e}),
newData
);
return result;
_initResponseObject: function (obj) {
var prop;
if (obj._response) {
for (prop in obj._response) {
if (obj._response.hasOwnProperty(prop)) {
delete obj._response[prop];
}
}
} else {
obj._response = {};
}
},
_initProgressObject: function (obj) {
var progress = {
loaded: 0,
total: 0,
bitrate: 0
};
if (obj._progress) {
$.extend(obj._progress, progress);
} else {
obj._progress = progress;
}
},
// Adds convenience methods to the data callback argument: _addConvenienceMethods: function (e, data) { var that = this, getPromise = function (args) { return $.Deferred().resolveWith(that, args).promise(); }; data.process = function (resolveFunc, rejectFunc) { if (resolveFunc || rejectFunc) { data._processQueue = this._processQueue = (this._processQueue || getPromise([this])).then( function () { if (data.errorThrown) { return $.Deferred() .rejectWith(that, [data]).promise(); } return getPromise(arguments); } ).then(resolveFunc, rejectFunc); } return this._processQueue || getPromise([this]); }; data.submit = function () { if (this.state() !== 'pending') { data.jqXHR = this.jqXHR = (that._trigger( 'submit', $.Event('submit', {delegatedEvent: e}), this ) !== false) && that._onSend(e, this); } return this.jqXHR || that._getXHRPromise(); }; data.abort = function () { if (this.jqXHR) { return this.jqXHR.abort(); } this.errorThrown = 'abort'; that._trigger('fail', null, this); return that._getXHRPromise(false); }; data.state = function () { if (this.jqXHR) { return that._getDeferredState(this.jqXHR); } if (this._processQueue) { return that._getDeferredState(this._processQueue); } }; data.processing = function () { return !this.jqXHR && this._processQueue && that ._getDeferredState(this._processQueue) === 'pending'; }; data.progress = function () { return this._progress; }; data.response = function () { return this._response; }; },
jquery.ui.widget.js里面的_trigger函数
this.element.trigger( event, data );
打印event
jQuery.Event {originalEvent: j…y.Event, type: "fileuploadadd", isDefaultPrevented: ƒ, timeStamp: 1561917127348, jQuery111304105261403454039: true, …}currentTarget: input#fileuploaddata: undefineddelegateTarget: input#fileuploaddelegatedEvent: jQuery.Event {originalEvent: Event, type: "change", isDefaultPrevented: ƒ, timeStamp: 33359.854999987874, jQuery111304105261403454039: true, …}handleObj: {type: "fileuploadadd", origType: "fileuploadadd", data: undefined, handler: ƒ, guid: 20, …}isDefaultPrevented: ƒ returnFalse()isTrigger: 3jQuery111304105261403454039: truenamespace: ""namespace_re: nulloriginalEvent: jQuery.Event {type: "add", delegatedEvent: j…y.Event, timeStamp: 1561917127348, jQuery111304105261403454039: true}result: undefinedtarget: input#fileuploadtimeStamp: 1561917127348type: "fileuploadadd"__proto__: Object
trigger后面的代码是
return !( $.isFunction( callback ) &&
callback.apply( this.element[0], [ event ].concat( data ) ) === false ||
event.isDefaultPrevented() );
callback对应下面的add
// The add callback is invoked as soon as files are added to the fileupload // widget (via file input selection, drag & drop or add API call). // See the basic file upload widget for more information: add: function (e, data) { if (e.isDefaultPrevented()) { return false; } var $this = $(this), that = $this.data('blueimp-fileupload') || $this.data('fileupload'), options = that.options; data.context = that._renderUpload(data.files) .data('data', data) .addClass('processing'); options.filesContainer[ options.prependFiles ? 'prepend' : 'append' ](data.context); that._forceReflow(data.context); that._transition(data.context); data.process(function () { //这里的process是在_addConvenienceMethods里面定义的 return $this.fileupload('process', data); }).always(function () { data.context.each(function (index) { $(this).find('.size').text( that._formatFileSize(data.files[index].size) ); }).removeClass('processing'); that._renderPreviews(data); }).done(function () { data.context.find('.start').prop('disabled', false); if ((that._trigger('added', e, data) !== false) && (options.autoUpload || data.autoUpload) && data.autoUpload !== false) { data.submit(); } }).fail(function () { if (data.files.error) { data.context.each(function (index) { var error = data.files[index].error; if (error) { $(this).find('.error').text(error); } }); } }); },
jquery.fileupload-ui.js里面的
_renderUpload: function (files) { return this._renderTemplate( this.options.uploadTemplate, //自己做测试的,这边是null files ); },
_renderTemplate: function (func, files) { if (!func) { return $(); //自己做测试,这边就返回了 } var result = func({ files: files, formatFileSize: this._formatFileSize, options: this.options }); if (result instanceof $) { return result; } return $(this.options.templatesContainer).html(result).children(); },
_forceReflow: function (node) { return $.support.transition && node.length && node[0].offsetWidth; },
_transition: function (node) { var dfd = $.Deferred(); if ($.support.transition && node.hasClass('fade') && node.is(':visible')) { node.bind( $.support.transition.end, function (e) { // Make sure we don't respond to other transitions events // in the container element, e.g. from button elements: if (e.target === node[0]) { node.unbind($.support.transition.end); dfd.resolveWith(node); } } ).toggleClass('in'); } else { node.toggleClass('in'); dfd.resolveWith(node); } return dfd; },
data.process = function (resolveFunc, rejectFunc) { if (resolveFunc || rejectFunc) { data._processQueue = this._processQueue = (this._processQueue || getPromise([this])).then( function () { if (data.errorThrown) { return $.Deferred() .rejectWith(that, [data]).promise(); } return getPromise(arguments); } ).then(resolveFunc, rejectFunc); } return this._processQueue || getPromise([this]); };
然后跳转jquery.fileupload-ui.js里面
// Processes the files given as files property of the data parameter, // returns a Promise object that allows to bind callbacks: process: function (data) { var that = this, options = $.extend({}, this.options, data); if (options.processQueue && options.processQueue.length) { this._transformProcessQueue(options); if (this._processing === 0) { this._trigger('processstart'); } $.each(data.files, function (index) { var opts = index ? $.extend({}, options) : options, func = function () { if (data.errorThrown) { return $.Deferred() .rejectWith(that, [data]).promise(); } return that._processFile(opts, data); }; opts.index = index; that._processing += 1; that._processingQueue = that._processingQueue.then(func, func) .always(function () { that._processing -= 1; if (that._processing === 0) { that._trigger('processstop'); } }); }); } return this._processingQueue; },
// Replaces the settings of each processQueue item that // are strings starting with an "@", using the remaining // substring as key for the option map, // e.g. "@autoUpload" is replaced with options.autoUpload: _transformProcessQueue: function (options) { var processQueue = []; $.each(options.processQueue, function () { var settings = {}, action = this.action, prefix = this.prefix === true ? action : this.prefix; $.each(this, function (key, value) { if ($.type(value) === 'string' && value.charAt(0) === '@') { settings[key] = options[ value.slice(1) || (prefix ? prefix + key.charAt(0).toUpperCase() + key.slice(1) : key) ]; } else { settings[key] = value; } }); processQueue.push(settings); }); options.processQueue = processQueue; },
processstart: function (e) { if (e.isDefaultPrevented()) { return false; } $(this).addClass('fileupload-processing'); },
然后是jquery.fileupload-process.js里面的
_processFile: function (data, originalData) { var that = this, dfd = $.Deferred().resolveWith(that, [data]), chain = dfd.promise(); this._trigger('process', null, data); $.each(data.processQueue, function (i, settings) { var func = function (data) { if (originalData.errorThrown) { return $.Deferred() .rejectWith(that, [originalData]).promise(); } return that.processActions[settings.action].call( that, data, settings ); }; chain = chain.then(func, settings.always && func); }); chain .done(function () { that._trigger('processdone', null, data); that._trigger('processalways', null, data); }) .fail(function () { that._trigger('processfail', null, data); that._trigger('processalways', null, data); }); return chain; },
// Processes the files given as files property of the data parameter, // returns a Promise object that allows to bind callbacks: process: function (data) { var that = this, options = $.extend({}, this.options, data); if (options.processQueue && options.processQueue.length) { this._transformProcessQueue(options); if (this._processing === 0) { this._trigger('processstart'); } $.each(data.files, function (index) { var opts = index ? $.extend({}, options) : options, func = function () { if (data.errorThrown) { return $.Deferred() .rejectWith(that, [data]).promise(); } return that._processFile(opts, data); }; opts.index = index; that._processing += 1; that._processingQueue = that._processingQueue.then(func, func) .always(function () { that._processing -= 1; if (that._processing === 0) { that._trigger('processstop'); } }); }); } return this._processingQueue; },
结论
绑定事件的时候依赖于this.options.filesContainer,这玩意不存在于jquery.fileupload.js里面
_initEventHandlers: function () { this._super(); var handlers = {}; handlers[this.options.uploadImageEditorPreviewSelector] = this._previewHandler.bind(this); console.log(`filesContainer = ${this.options.filesContainer}`); this._on(this.options.filesContainer, handlers); },
而filesContainer在jquery.fileupload-ui.js中
// The container for the list of files. If undefined, it is set to // an element with class "files" inside of the widget element: filesContainer: undefined, _initFilesContainer: function () { var options = this.options; if (options.filesContainer === undefined) { options.filesContainer = this.element.find('.files'); } else if (!(options.filesContainer instanceof $)) { options.filesContainer = $(options.filesContainer); } },
因为basic.html没有引用jquery.fileupload-ui.js,所以导致事件绑定失败。
顺便打印一下this.element
console.log(`filesContainer = ${this.options.filesContainer}`);
console.log(this.element);
自己手动复制函数,然后在_initEventHandlers里面调用
_initFilesContainer: function () { var options = this.options; if (options.filesContainer === undefined) { options.filesContainer = this.element.find('.files'); } else if (!(options.filesContainer instanceof $)) { options.filesContainer = $(options.filesContainer); } }, _initEventHandlers: function () { this._super(); this._initFilesContainer(); var handlers = {}; handlers[this.options.uploadImageEditorPreviewSelector] = this._previewHandler.bind(this); this._on(this.options.filesContainer, handlers); },
页面加载后,进行查询
$('#fileupload').fileupload('option', 'filesContainer')
在还有jquery.fileupload-ui.js里面还有关于uploadTemplateID的设置
options: {
// By default, files added to the widget are uploaded as soon
// as the user clicks on the start buttons. To enable automatic
// uploads, set the following option to true:
autoUpload: false,
// The ID of the upload template:
uploadTemplateId: 'template-upload',
// The ID of the download template:
downloadTemplateId: 'template-download',
作者:Chuck Lu GitHub |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
2016-07-01 Controller methods and views
2016-07-01 Working with SQL Server LocalDB
2016-07-01 ASP.NET Razor - C# Variables
2016-07-01 ASP.NET Razor - C# and VB Code Syntax
2015-07-01 Complete The Pattern #1
2015-07-01 Complete The Pattern #2
2015-07-01 Complete The Pattern #6 - Odd Ladder