Let the code inserted into the innerHTML script run during ajax programming, we often need to assign the page content obtained by xmlhttp to a container (such as p, span, or td) through innerHTML, but there is a problem here, that is, if the page content we will assign to innerHTML contains scripts, these scripts, whether external scripts or internal scripts, may not be executed (1. This problem may be insignificant or even negligible in some cases, but sometimes it is very serious, and it is likely that our program will not get the expected results. Therefore, we need to solve this problem.
If you have read MSDN, you will find that not all scripts inserted into innerHTML cannot be executed. If the script tag of this script contains the defer attribute, IE will correctly execute these scripts. Unfortunately, Moziila, Firefox, and Opera do not use this set. No matter whether the script tag has the defer attribute set, these browsers will not execute scripts inserted into innerHTML like IE.
But no matter whether the script is executed or not, we can be certain that these scripts are indeed inserted into innerHTML. If you don't believe them, you can use alert to check them out. However, if you are alert, you may also find an exception, that is, if the script starts with the innerHTML content, the IE browser will ignore this script, moziila, Firefox, and Opera do not.
Now, the problem analysis is almost done. Let's take a look at how to solve it.
The solution is actually very simple. It is to extract all the scripts inserted into innerHTML and execute them one by one. However, we need to solve the above two problems first.
First, let's look at the first question. How can we avoid repeated execution of scripts with the defer attribute in innerHTML in IE. This is easy. You only need to determine whether the browser is IE and then check whether the script to be executed has the defer attribute. It should be noted that when determining the IE browser, we need to avoid being recognized and spoofed by the opera Browser. In the subsequent code, we will see how it works.
Next, let's look at the problem that IE ignores the script starting with innerHTML, which is also easy to solve. You only need to append a piece of content that is not a script at the beginning of the content to be inserted into innerHTML. However, do not try to append a label with null content, or use spaces, carriage return, or line breaks. This will not work, and the script at the beginning will still be ignored. Do not try to append the script. Although this can make the script at the beginning no longer be ignored, it will still affect the display of the original content. Although you may not think it is obvious, for the picky user, this may be intolerable. Therefore, in order to ensure that the added content can not only prevent the start script from being ignored, but also avoid adverse effects, we will add the following content:
Hack ie
Although the content above has a certain length, it is not displayed, and the inserted label does not have an id or name, therefore, it does not conflict with the id or name of some labels in the original content. However, it should be noted that you should determine whether the content is IE or not, and then decide not to add the content, because some other browsers may not support display: none (for example, Opera Mini). If you add this code, the final display effect will be affected.
Next let's take a look at how to extract the script and execute it.
It is easy to retrieve the script. You only need to use the getElementsByTagName method of the object where innerHTML is located. This method works for almost all container labels. After the scripts are taken out, we need to determine whether they are external scripts or internal scripts.
First, let's look at the external script. If it is an external script, we chose this method, that is, first create a copy object for this external script, and set its defer attribute to true (this is to allow the IE browser to execute it correctly), and then insert the copy object to the head using the appendChild method. Here you may ask, why is it not inserted into the object where innerHTML is located? Isn't it better to insert it into the object where innerHTML is located? If you try it, you will know that if you insert it into the object where innerHTML is located, there will be no problem in IE browser, but there will be some problems in Mozilla/Firefox and Opera browsers. The problem is that if you do this on Firefox, the browser will stop responding (this is the test result on Firefox 1.5, and it is unknown if other versions have this problem), while on Opera, the script is inexplicably executed twice (this is the test result on Opera 8.5, and it is unknown whether the problem occurs in other versions of Opera ). To avoid these problems, I chose to insert them into the head.
Let's look at the internal script. We can directly obtain the content of the internal script using the text attribute of the script object. Here we use the text attribute of the script object instead of the innerHTML attribute because in the Opera browser, the innerHTML attribute of the script object is empty. Only the text attribute can be used to obtain the script content. Run the internal script and run eval directly. However, the script may be included in the HTML comment tag. Therefore, we need to remove the comment tag first, otherwise an error will occur in IE.
The above analysis looks perfect, but there is still a problem, one is document. write and document. the writeln problem. In Blueidea, bound0 provides an idea to replace the default document. write and document. the writeln method, however, uses string replacement. Therefore, it is only valid for internal scripts and cannot be used for external scripts. Therefore, I thought of a more general method, that is, directly replacing the document. write and document. re-define writeln, so that no matter whether the internal script or external script is executed, it is our own document. write and document. writeln. However, there are also some side effects, that is, these two functions cannot be used in the current page as before, but these two functions will not be used after the page is loaded, so the side effects of redefining them are very small. However, we still cannot guarantee document. write or document. the content output by writeln is displayed in the most appropriate position. It only attaches the content to the container where the content is placed.
Another problem is caused by eval. One is the scope problem of hutia on Blueidea, and the other is that if the internal script executed by eval is used, the internal script will be executed before the external script is loaded. To solve these two problems, you can use window. the setTimeout function allows every script to be executed after a delay of a period. The external script delay can be set to a long time to ensure full loading, while the internal script can be set to a short time, because the execution time of a script is usually very short, this ensures that the scope is not changed, it can basically ensure that the script execution sequence will not change (this method may not necessarily be 100% effective to ensure the execution sequence. If the network is very busy, external Scripts may not be loaded in the set time, but at least it is much better than directly using eval ).
-----------
(1) Note: Here, we use the qualifier "possible", because the script will be executed in one case and you will see this situation in the following article.
If the preceding method is used, most scripts can run normally. However, if the script contains the defer attribute, IE will run the Code itself (previously mentioned), so it will disrupt the execution order. In addition, the code written by document. write and document. writeln is added back to the end, rather than the script location. This is also a problem.
To solve these two problems, we need to make some changes to the preceding solution. First, we can't assign the content to innerHTML first, and then use it to retrieve the script. We need to analyze the content directly to retrieve the script. In addition, HTML parts other than the script cannot be directly assigned to innerHTML. After the script is executed. write \ writeln content is merged in order and then assigned to innerHTML. Note that we cannot partially connect the content to the end of innerHTML, because there may be half of the TAG content, in this case, the browser is prone to errors. In addition, you will see repeated page refreshes. If the buffer is first placed and the last time it is assigned to innerHTML, this problem will not occur.
In addition, when the script is executed, you can check whether there are new scripts in the buffer. If so, Recursive Execution can solve the problem of document. write and document. the script written by writeln can also be executed.
Update:
Fixed the problem that the script inserted into innerHTML could not obtain the objects inserted into innerHTML. (Thank you for your reminder ).
Added a shared lock for the content in the same container, so that continuous settings in the same container will not conflict. (Thanks to Jason Li, a Singaporean user ).
Update:
The external script cache function is added to increase the speed of loading the same external script for the second time.
Update:
With the reminder of an enthusiastic user johnZEN, shared locks are added so that content in multiple containers will not conflict with each other at the same time.
With the reminder of user udbjatwfn, the internal script execution scope error in IE was corrected.
The following is the final implementation code:
Download: innerhtml. js
The Code is as follows:
/* Innerhtml. js
* Copyright Ma Bingyao
* Version: 1.9
* LastModified: 2006-06-04
* This library is free. You can redistribute it and/or modify it.
*#
*/
Var global_html_pool = [];
Var global_script_pool = [];
Var global_script_src_pool = [];
Var global_lock_pool = [];
Var innerhtml_lock = null;
Var document_buffer = "";
Function set_innerHTML (obj_id, html, time ){
If (innerhtml_lock = null ){
Innerhtml_lock = obj_id;
}
Else if (typeof (time) = "undefined "){
Global_lock_pool [obj_id + "_ html"] = html;
Window. setTimeout ("set_innerHTML ('" + obj_id + "', global_lock_pool ['" + obj_id + "_ html']);", 10 );
Return;
}
Else if (innerhtml_lock! = Obj_id ){
Global_lock_pool [obj_id + "_ html"] = html;
Window. setTimeout ("set_innerHTML ('" + obj_id + "', global_lock_pool ['" + obj_id + "_ html']," + time + ");", 10 );
Return;
}
Function get_script_id (){
Return "script _" + (new Date (). getTime (). toString (36)
+ Math. floor (Math. random () * 100000000). toString (36 );
}
Document_buffer = "";
Document. write = function (str ){
Document_buffer + = str;
}
Document. writeln = function (str ){
Document_buffer + = str + "\ n ";
}
Global_html_pool = [];
Var scripts = [];
Html = html. split (/<\/script>/I );
For (var I = 0; I Global_html_pool [I] = html [I]. replace (/ Scripts [I] = {text: '', src :''};
Scripts [I]. text = html [I]. substr (global_html_pool [I]. length );
Scripts [I]. src = scripts [I]. text. substr (0, scripts [I]. text. indexOf ('>') + 1 );
Scripts [I]. src = scripts [I]. src. match (/src \ s * = \ s * (\ "([^ \"] *) \ "| \ '([^ \'] *) \ '| ([^ \ s] *) [\ s>])/I );
If (scripts [I]. src ){
If (scripts [I]. src [2]) {
Scripts [I]. src = scripts [I]. src [2];
}
Else if (scripts [I]. src [3]) {
Scripts [I]. src = scripts [I]. src [3];
}
Else if (scripts [I]. src [4]) {
Scripts [I]. src = scripts [I]. src [4];
}
Else {
Scripts [I]. src = "";
}
Scripts [I]. text = "";
}
Else {
Scripts [I]. src = "";
Scripts [I]. text = scripts [I]. text. substr (scripts [I]. text. indexOf ('>') + 1 );
Scripts [I]. text = scripts [I]. text. replace (/^ \ s * <\! -- \ S */g ,"");
}
}
Var s;
If (typeof (time) = "undefined "){
S = 0;
}
Else {
S = time;
}
Var script, add_script, remove_script;
For (var I = 0; I <scripts. length; I ++ ){
Var add_html = "document_buffer + = global_html_pool [" + I + "]; \ n ";
Add_html + = "document. getElementById ('" + obj_id + "'). innerHTML = document_buffer; \ n ";
Script = document. createElement ("script ");
If (scripts [I]. src ){
Script. src = scripts [I]. src;
If (typeof (global_script_src_pool [script. src]) = "undefined "){
Global_script_src_pool [script. src] = true;
S + = 2000;
}
Else {
S + = 10;
}
}
Else {
Script. text = scripts [I]. text;
S + = 10;
}
Script. defer = true;
Script. type = "text/javascript ";
Script. id = get_script_id ();
Global_script_pool [script. id] = script;
Add_script = add_html;
Add_script + = "document. getElementsByTagName ('head'). item (0 )";
Add_script + = ". appendChild (global_script_pool ['" + script. id + "']); \ n ";
Window. setTimeout (add_script, s );
Remove_script = "document. getElementsByTagName ('head'). item (0 )";
Remove_script + = ". removeChild (document. getElementById ('" + script. id + "'); \ n ";
Remove_script + = "delete global_script_pool ['" + script. id + "']; \ n ";
Window. setTimeout (remove_script, s + 10000 );
}
Var end_script = "if (document_buffer.match (/<\/script>/I) {\ n ";
End_script + = "set_innerHTML ('" + obj_id + "', document_buffer," + s + "); \ n ";
End_script + = "} \ n ";
End_script + = "else {\ n ";
End_script + = "document. getElementById ('" + obj_id + "'). innerHTML = document_buffer; \ n ";
End_script + = "innerhtml_lock = null; \ n ";
End_script + = "}";
Window. setTimeout (end_script, s );
}