Ps: flash achieves much better results, but this is not the scope of my research and there is no comparability.
Compatible with: ie6/7/8, firefox 3.5.5, opera 10.01, safari 4.0.3, chrome 3.0
Effect Preview
File Upload |
Select File |
Rename |
Operation |
Status |
|
|
Reset |
Select File |
|
|
Reset |
Select File |
|
|
Reset |
Select File |
|
Ps: Because the background is required, download the instance test to test the system.
Ps2: In the complete instance file, there is also a file attribute to view the instance.
Program description
[Upload]
The most important method in the program is upload. You can call it to perform a refreshing upload.
The upload process is like this. First stop the last upload and determine whether to select a file.
Call _ setIframe, _ setForm, and _ setInput respectively to generate the required iframe, form, and input.
If the timeout attribute is set, the timer is automatically set:Copy codeThe Code is as follows: if (this. timeout> 0 ){
This. _ timer = setTimeout ($ F. bind (this. _ timeout, this), this. timeout * 1000 );
}
Ps: after testing, if the latency is less than 0, ie will cancel the execution, while other browsers will treat the execution as 0.
The program has a _ sending attribute to determine the upload status.
When stop, dispose, _ finis, and _ timeout, it is set to false.
Before the upload starts, set it to true.
The final form is submitted and the upload starts.
[Iframe]
The program uses the _ setIframe function to create an iframe for refreshing the new image.
Because the iframe name in ie cannot be modified, you need to create iframe as follows:Copy codeThe Code is as follows: var iframename = "QUICKUPLOAD _" + QuickUpload. _ counter ++,
Iframe = document. createElement ($ B. ie? "<Iframe name = \" "+ iframename +" \ ">": "iframe ");
Iframe. name = iframename;
Iframe. style. display = "none ";
Ps: For more information about the iframe name, see the iframe section.
You can modify the name of ie8, but it cannot be modified in non-standard (strange) mode.
Here, a QuickUpload function's own _ counter attribute is used as a calculator, which ensures that the iframe names of each instance will not be repeated.
In order to execute the callback function after the file is uploaded, The _ finish function is executed in the onload of iframe:Copy codeThe Code is as follows: var finish = this. _ fFINISH = $ F. bind (this. _ finish, this );
If ($ B. ie ){
Iframe. attachEvent ("onload", finish );
} Else {
Iframe. onload = $ B. opera? Function () {this. onload = finish;}: finish;
}
In ie, you need to use attachEvent to bind onload, because setting onload directly in ie is invalid.
Besides attachEvent, onreadystatechange can also be used.
I am not clear about the reason. For details, refer to "the perfect method for judging whether iframe is loaded ".
There is another problem with loading iframe. Test the following code:
Copy codeThe Code is as follows: <body> <div id = "msg"> Status: </div> </body>
<Script>
Var msg = document. getElementById ("msg ");
Var iframe = document. createElement ("iframe ");
Iframe. onload = function () {msg. innerHTML + = "onload ,";}
Document. body. appendChild (iframe );
Iframe. src = "http://cloudgamer.cnblogs.com /"
</Script>
As a result, both safari and chrome trigger onload twice, while opera, ff, and ie (compatible with each other) are both once.
It is estimated that safari and chrome will be loaded for the first time after appendChild, and the loading is completed before src is set, so it is triggered twice.
If you set a src (except a null value) for iframe before inserting the body, the first loading is extended indirectly, and only once is triggered.
Ps: src without setting or null values is equivalent to link to "about: blank" (blank page ).
Therefore, opera, ff, and ie may be loading too slowly for the first time, and overwrite for the first time. Therefore, only one onload is triggered.
Ps: it may also be another reason, such as browser optimization. I'm not sure.
To solve the problem of loading too fast, you can determine whether to upload the status based on _ sending during onload.
Even if it is not tested, will there be a situation where the first onload is triggered just before submit after _ sending settings?
In the upload method, _ sending is placed after submit.
What if onload is triggered after the submit _ sending setting? (... Success)
This is basically not the case. If it does, put the _ sending setting in front of the submit.
Opera has another trouble. Test the following code:Copy codeThe Code is as follows: <body>
<Div id = "msg"> Status: </div>
<Form action = "http://cloudgamer.cnblogs.com/" target = "ifr">
</Form>
</Body>
<Script>
Var msg = document. getElementById ("msg ");
Var iframe = document. createElement ("iframe ");
Iframe. name = "ifr ";
Iframe. onload = function () {msg. innerHTML + = "onload ,";}
Document. body. appendChild (iframe );
Msg. innerHTML + = "submit ,";
Document. forms [0]. submit ();
</Script>
Ie and ff show submit, onload, safari and chrome show onload, submit, onload, consistent with the above analysis.
Opera shows that submit, onload, and onload are triggered after submit.
In this case, we cannot simply use _ sending.
Is it because submit cannot cancel iframe loading?
Set a src before appendChild. If the result is normal, only onload is triggered once. It seems yes.
Although I don't know the reason, there are still some solutions. One is to set a src before appendChild, And you can reset onload In the first onload, as in the program.
However, there are uncertainties in both methods, and they cannot completely solve the problem, but they cannot find a better method.
There is another problem with the onload of ff. If a server error such as ERROR_INTERNET_CONNECTION_RESET (the file size exceeds the server limit) occurs, the onload will not be triggered even after loading. A solution cannot be found for the moment.
One drawback of iframe is that it can only use onload to determine whether the loading is complete, but there is no way to determine whether the loading is successful.
There is no such thing as the status of XMLHTTP, and there is no way to identify errors such as 404.
This must be done well during use, such as the size of the file to be uploaded, the time-out period, and how to handle the problem of no response for a long time.
[Form]
The program uses the _ setForm function to create a form for data submission.
To enable the upload without refreshing, perform special processing on the form:Copy codeThe Code is as follows: $. extend (form ,{
Target: this. _ iframe. name, method: "post", encoding: "multipart/form-data"
});
Ps: For more information, see the Skip upload section.
Because form is manually inserted, you need to set the form style to make it "invisible" to avoid affecting the original page layout:Copy codeThe Code is as follows: $ D. setStyle (form ,{
Padding: 0, margin: 0, border: 0,
BackgroundColor: "transparent", display: "inline"
});
Note that the same form control can only correspond to one form.
If the file control already has a form, it must be removed before submission:
File. form & $ E. addEvent (file. form, "submit", $ F. bind (this. dispose, this ));
The dispose method is used to destroy programs, including removing form.
Ps: If the submit is overwritten before submission, manually execute the dispose method once.
Finally, insert form into the dom:
File. parentNode. insertBefore (form, file). appendChild (file );
Insert the form into the file control first, and then insert the file into the form to ensure that the file is in the original position.
[Input]
If other parameters are to be passed, the program uses the _ setInput function to create a Form Control for passing data.
Because the generated form only contains the file control, other parameters must be generated by the program.
The program uses a _ inputs set to save the form control currently generated in the form.
First, create a Form Control Based on the custom parameter property:
Copy codeThe Code is as follows: for (name in this. parameter ){
Var input = form [name];
If (! Input ){
Input = document. createElement ("input ");
Input. name = name; input. type = "hidden ";
Form. appendChild (input );
}
Input. value = this. parameter [name];
NewInputs [name] = input;
Delete oldInputs [name];
}
When the form does not have a control with the corresponding name, a hidden control is automatically generated and inserted into the form.
NewInputs is used to record the currently generated controls, while oldInputs is the _ inputs set.
After the control with the corresponding name is set, the association of the control is deleted from oldInputs.
Then remove the controls associated with oldInputs:
For (name in oldInputs) {form. removeChild (oldInputs [name]);}
In this way, the useless control generated last time can be removed.
Finally, record the current control to _ inputs for later use.
[Stop]
To stop the current upload operation, you can call the stop method.
Generally, when an iframe overload occurs, the last load will be canceled. You only need to reset src to cancel the upload.
Test the following code:
Copy codeThe Code is as follows: <body>
<Iframe id = "ifr" name = "ifr"> </iframe>
<Form action = "http://cloudgamer.cnblogs.com/" target = "ifr">
</Form>
</Body>
<Script>
Document. forms [0]. submit ();
Document. getElementById ("ifr"). src = "";
</Script>
The results all cancel loading. Except opera, what is the reason.
There are two solutions: one is to use form to submit it once with an action, and the other is to directly remove iframe.
The next method is more convenient. in the program, use the _ removeIframe method to directly remove iframe.
Ps: Let me know if there is a better way.
[Dispose]
You can call the dispose method to destroy a program after it is used or for other reasons.
In dispose, we mainly remove iframe and form.
The _ removeIframe method is used to remove iframe. First, the onload is removed, and then the iframe is removed from the body:
Var iframe = this. _ iframe;
$ B. ie? Iframe. detachEvent ("onload", this. _ fFINISH): (iframe. onload = null );
Document. body. removeChild (iframe); this. _ iframe = null;
Very simple, but there is a problem with ff, test the following code:Copy codeThe Code is as follows: <form target = "ifr" action = "x">
<Input id = "btn" type = "submit" value = "click">
</Form>
<Iframe name = "ifr" id = "ifr"> </iframe>
<Script>
Document. getElementById ("btn"). onclick = function (){
Document. getElementById ("ifr"). onload = function (){
This. parentNode. removeChild (this );
};
}
</Script>
Iframe can be removed after submission, but ff still displays the "loading" status.
However, the solution is also very simple. use setTimeout to set a latency so that iframe can be executed completely.
Therefore, in dispose, _ removeIframe is called as follows:Copy codeThe Code is as follows: if ($ B. firefox ){
SetTimeout ($ F. bind (this. _ removeIframe, this), 0 );
} Else {
This. _ removeIframe ();
}
As for form removal, it is relatively simple to do so in _ removeForm:Copy codeThe Code is as follows: var form = this. _ form, parent = form. parentNode;
If (parent ){
Parent. insertBefore (this. file, form); parent. removeChild (form );
}
This. _ form = this. _ inputs = null;
Determine the parentNode. Otherwise, if the parentNode does not exist, the subsequent execution will fail.
[File reset]
In the instance, there is a ResetFile function used to reset the file control.
The general method to reset the file control is to execute reset for the form, but the problem is that other form controls are also reset.
Previously, due to security issues, the file value cannot be modified.
But now ff, chrome and safari can set it to a null value for resetting:
File. value = "";
Of course, other values are not allowed.
Ps: I can't remember it before.
For opera, there is a work und using its type attribute:
File. type = "text"; file. type = "file ";
By modifying the type of the file control, the value is automatically restored to a null value, which indirectly clears the file control.
Ps: This method can be used to indirectly obtain the file path, but it is useless because the value is cleared after it is changed back.
The type of the Form Control of ie cannot be modified, and the opera method cannot be used.
However, there are still the following solutions:
1. Create a new form, insert the file into the reset, and then remove it:Copy codeThe Code is as follows: with (file. parentNode. insertBefore (document. createElement ('form'), file )){
AppendChild (file); reset (); removeNode (false );
}
The advantage is that the native reset is stable and reliable, but the efficiency is low.
Ps: removeNode is only supported by ie and opera. To be compatible, use removeChild.
2. Use outerHTML to recreate a file control:
File. outerHTML = file. outerHTML;
The advantage is that it is efficient, but because it is a newly created file control, all previously associated items are lost.
Ps: ff does not support outerHTML.
3. Use cloneNode to copy a file control:
File. parentNode. replaceChild (file. cloneNode (false), file );
It is similar to the same method, but the efficiency is lower.
4. Use the select method to select the text field of the file control and then clear it:
File. select (); document. selection. clear ();
Or
File. select (); document. selection. clear ();
It seems that there is no problem, but the file must be able to be select (it cannot be a hidden state ).
Ps: both methods can only be used in ie.
Because file in the program needs to be associated, neither method 2 nor method 3 can be used.
Method 4 looks good, but there is a fatal problem. Test the following code in ie:Copy codeThe Code is as follows: <form> <input id = "test" name = "file" type = "file"> </form>
<Script>
Document. getElementById ("test"). onchange = function (){
This. select (); document. selection. clear ();
This. form. submit ();
}
</Script>
When the request is executed to submit, the error "Access Denied" is displayed. The reason is unclear. I do not know whether it is ie's intentional or bug.
It seems that only method 1 can be used:Copy codeThe Code is as follows: function ResetFile (file ){
File. value = ""; // ff chrome safari
If (file. value ){
If ($ B. ie) {// ie
With (file. parentNode. insertBefore (document. createElement ('form'), file )){
AppendChild (file); reset (); removeNode (false );
}
} Else {// opera
File. type = "text"; file. type = "file ";
}
}
}
Ps: Let me know if there is a better way.
This function is not general enough. It is best to select the desired method based on the actual situation.
Tips
[Number of uploaded files]
In a file upload instance, each file is uploaded simultaneously.
After testing, the number of files simultaneously uploaded by the browser is as follows:
Ie 2
Ff 8
Opera 8
Chrome 6
Safari 6
Since ie can only upload 2 files at the same time, you can only set more files to queue, but not to achieve simultaneous upload.
Ps: it is only a visual test result. If there are any errors, please submit them.
[Transfer Parameters]
In an upload file instance, you can pass the corresponding modified file name. When multiple files are uploaded together, the corresponding file name can also be found.
Because after the form control value is passed to the background, the order of the obtained data is the same as that of the foreground form control.
You only need to ensure that the file control in the foreground is in the same order as the corresponding form control, you can use this feature to obtain the corresponding value.
For details, refer to the background code.
[Callback function]
There are two methods to respond to the callback function after the upload is completed.
One is to output and execute the callback function in iframe after the background upload is complete, or call the callback function in the parent window through parent.
This is convenient, but must be processed in iframe, such as viewing the instance of file properties.
The other is to execute the callback function in the onload of iframe.
The advantage is that all processing can be placed in the parent window, and iframe can not be processed or used to feedback information.
The disadvantage is that there is a compatibility problem, and there will be situations where onload is not triggered after loading (the above iframe section describes ).
In the upload file instance, data output in iframe is processed in onFinish.
Because there may be some unexpected situations that may cause a long response or even no response, you must set a timeout in case.
[Process returned Data]
As mentioned above, data output in iframe can be processed in onFinish.
To obtain data from the iframe body, you can use the following methods:
Iframe.contentdomaindoc ument. body. innerHTML
Iframe. contentDocument. body. innerHTML
Too many frames[iframenameapps.doc ument. body. innerHTML
The first two types are similar. The latter is relatively simple, but ie does not support contentDocument. Unfortunately.
The third method is to use the frames object. Note that the obtained object is the window object directly.
Because the program can directly obtain the iframe object, the first method is used.
However, there is a problem mentioned in the iframe section, that is, the error message page returned.
In the upload file instance, the file information data in the iframe format is output.
In onFinish, the processing is as follows:Copy codeThe Code is as follows: try {
Var info = eval ("(" + iframe.content+doc ument. body. innerHTML + ")");
Show ("uploaded ");
} Catch (e ){
Show ("Upload Failed"); stop (); return;
}
Only data in the correct json format can be returned for normal operation. Otherwise, an error will be thrown, and error messages such as 404 will be excluded from each other.
Ps: If you have a better method, please submit it.
[Destruction program]
There are many dom operations in the program. It is best to execute a dispose method to destroy the program when you do not need to continue using it.
For example, after the file is removed, before the window is closed, before the form is submitted, and before the form element is calendar.
It can save resources, prevent dom Memory leakage, and avoid conflicts during form nesting.
[Availability]
After reading "ppk talking about javascript", we paid more attention to availability.
Upload instances can also be uploaded normally if the browser does not support js. You can test them by yourself.
[Encoding]
In the last non-Refresh upload system, many people reported that the uploaded file name is garbled and later found to be an encoding problem.
When transmitting Chinese information, note that the front and back-end encoding must be unified, including charset, file encoding, and web. config configuration.
[Asp version]
Asp and. net have the same functions.
However, if a file control with the same name is submitted due to a defect in the upload class, an error occurs. After modification, the file control can be used normally.
Instructions for use
During instantiation, the first required parameter is the file control object:
New QuickUpload (file );
The second optional parameter is used to set the default attributes of the system, including
Attribute: Default Value // description
Parameter :{}, // parameter object
Action: "", // set action
Timeout: 0, // set timeout (in seconds)
OnReady: function () {}, // executed during upload preparation
OnFinish: function () {}, // executed when the upload is complete
OnStop: function () {}, // executed when the upload is stopped
OnTimeout: function () {}// executed when the upload times out
The following methods are also provided:
Upload: Perform the upload operation;
Stop: stop the upload operation;
Dispose: destroys a program.
Program source codeCopy codeThe Code is as follows:
Var QuickUpload = function (file, options ){
This. file =$ $ (file );
This. _ sending = false; // whether the file is being uploaded
This. _ timer = null; // timer
This. _ iframe = null; // iframe object
This. _ form = null; // form object
This. _ inputs = {}; // input object
This. _ fFINISH = null; // complete the execution function
$. Extend (this, this. _ setOptions (options ));
};
QuickUpload. _ counter = 1;
QuickUpload. prototype = {
// Set the default attribute
_ SetOptions: function (options ){
This. options = {// Default Value
Action: "", // set action
Timeout: 0, // set timeout (in seconds)
Parameter :{}, // parameter object
OnReady: function () {}, // executed during upload preparation
OnFinish: function () {}, // executed when the upload is complete
OnStop: function () {}, // executed when the upload is stopped
OnTimeout: function () {}// executed when the upload times out
};
Return $. extend (this. options, options || {});
},
// Upload a file
Upload: function (){
// Stop the last upload
This. stop ();
// No file is returned
If (! This. file |! This. file. value) return;
// Attributes may be modified in onReady.
This. onReady ();
// Set iframe, form, and form Controls
This. _ setIframe ();
This. _ setForm ();
This. _ setInput ();
// Set timeout
If (this. timeout> 0 ){
This. _ timer = setTimeout ($ F. bind (this. _ timeout, this), this. timeout * 1000 );
}
// Start upload
This. _ form. submit ();
This. _ sending = true;
},
// Set iframe
_ SetIframe: function (){
If (! This. _ iframe ){
// Create an iframe
Var iframename = "QUICKUPLOAD _" + QuickUpload. _ counter ++,
Iframe = document. createElement ($ B. ie? "<Iframe name = \" "+ iframename +" \ ">": "iframe ");
Iframe. name = iframename;
Iframe. style. display = "none ";
// Records the completed program for easy removal
Var finish = this. _ fFINISH =$ $ F. bind (this. _ finish, this );
// Execute the completed program after loading iframe
If ($ B. ie ){
Iframe. attachEvent ("onload", finish );
} Else {
Iframe. onload = $ B. opera? Function () {this. onload = finish;}: finish;
}
// Insert the body
Var body = document. body; body. insertBefore (iframe, body. childNodes [0]);
This. _ iframe = iframe;
}
},
// Set form
_ SetForm: function (){
If (! This. _ form ){
Var form = document. createElement ('form'), file = this. file;
// Set attributes
$. Extend (form ,{
Target: this. _ iframe. name, method: "post", encoding: "multipart/form-data"
});
// Set the style
$ D. setStyle (form ,{
Padding: 0, margin: 0, border: 0,
BackgroundColor: "transparent", display: "inline"
});
// Remove form before submission
File. form & $ E. addEvent (file. form, "submit", $ F. bind (this. dispose, this ));
// Insert form
File. parentNode. insertBefore (form, file). appendChild (file );
This. _ form = form;
}
// Action may be modified
This. _ form. action = this. action;
},
// Set input
_ SetInput: function (){
Var form = this. _ form, oldInputs = this. _ inputs, newInputs ={}, name;
// Set input
For (name in this. parameter ){
Var input = form [name];
If (! Input ){
// Create a new input
Input = document. createElement ("input ");
Input. name = name; input. type = "hidden ";
Form. appendChild (input );
}
Input. value = this. parameter [name];
// Record the current input
NewInputs [name] = input;
// Delete existing records
Delete oldInputs [name];
}
// Remove useless input
For (name in oldInputs) {form. removeChild (oldInputs [name]);}
// Save the current input
This. _ inputs = newInputs;
},
// Stop upload
Stop: function (){
If (this. _ sending ){
This. _ sending = false;
ClearTimeout (this. _ timer );
// Reset iframe
If ($ B. opera) {// opera may be faulty by setting src
This. _ removeIframe ();
} Else {
This. _ iframe. src = "";
}
This. onStop ();
}
},
// Destroy the program
Dispose: function (){
This. _ sending = false;
ClearTimeout (this. _ timer );
// Clear iframe
If ($ B. firefox ){
SetTimeout ($ F. bind (this. _ removeIframe, this), 0 );
} Else {
This. _ removeIframe ();
}
// Clear form
This. _ removeForm ();
// Clear dom Association
This. _ inputs = this. _ fFINISH = this. file = null;
},
// Clear iframe
_ RemoveIframe: function (){
If (this. _ iframe ){
Var iframe = this. _ iframe;
$ B. ie? Iframe. detachEvent ("onload", this. _ fFINISH): (iframe. onload = null );
Document. body. removeChild (iframe); this. _ iframe = null;
}
},
// Clear form
_ RemoveForm: function (){
If (this. _ form ){
Var form = this. _ form, parent = form. parentNode;
If (parent ){
Parent. insertBefore (this. file, form); parent. removeChild (form );
}
This. _ form = this. _ inputs = null;
}
},
// Timeout Function
_ Timeout: function (){
If (this. _ sending) {this. _ sending = false; this. stop (); this. onTimeout ();}
},
// Complete the Function
_ Finish: function (){
If (this. _ sending) {this. _ sending = false; this. onFinish (this. _ iframe );}
}
}
Download complete instance
Download the complete instance (asp version)
Related Applications: JavaScript Image Upload preview Effect
Reprinted please indicate the source: http://www.cnblogs.com/cloudgamer/