2

I'm building an infinite scrolling routine that caches rows to add off-page.

I $.get the rows and add them to a $('<div/>'), then whenever I need a new row, I add one from that dom to the page.

I've read that when adding to the dom jquery will strip out the script tags and execute the javascript then.

This seems to be creating a problem for me as when jquery runs the script the elements are in the off-page dom, not in the main page dom.

How can I keep the script around so that it gets executed when I add an off-page element to the page?

I've added alerts in and the javascript never seems to get executed at all, either when adding to the off page dom, or the actual page dom, so maybe i'm failing before that becomes a problem.


Code to get new rows and place them in an off-page dom:

var nextPageDOM = $('<div/>');
var jqxhr = $.get(data.nextPageURL, function(nextPageHTML) {
    nextPageDOM.html($(nextPageHTML).find(data.settings.pagedContent));

    // Get the elements to add to the page
    data.nextPageElements = nextPageDOM.find(data.settings.rowSelector);
}

Code to add from off-page DOM to the page:

var elementToAdd = data.nextPageElements.first().clone();
$(data.settings.pagedContent).append(elementToAdd);
George Duckett
  • 31,770
  • 9
  • 95
  • 162
  • actually I haven't got the question, what is the main problem? what's going wrong when you appending `elementToAdd`? – haynar Sep 21 '12 at 10:05
  • Uhm... do your $.get return with success? Have you checked nextPageHTML value on return? Did you detect on which code line the execution stops? – Bardo Sep 21 '12 at 10:19
  • @Bardo: The get does return correctly. The `nextPageHTML` does include the script as expected. What might be something though is that the after `nextPageDOM.html($(nextPageHTML));` the `nextPageDOM` doesn't include any `script` tags. – George Duckett Sep 21 '12 at 10:36
  • @haynar: The script elements in the original get response aren't being ran. – George Duckett Sep 21 '12 at 10:37

4 Answers4

3

It's very difficult to solve not knowing where does many variables point, so if we suppose that data.settings.pagedContent points to the on-page DOM, why finding only the elements which equals what you already have in the ajax response? If in the other hand data.settings.pagedContent is a selector, i'm wondering if the scripts are getting outside the selector. Would be very useful if you provide an ajaxresponse example.

Anyway, if you are looking for a way to avoid the jQuery script stripping, you can do the next: Create a secondary div container

var $cont = $("<div/>");

insert the ajax response into it via innerHTML (hope it's well-formed)

$cont[0].innerHTML = nextPageHTML;

then select the scripts from $cont and save them

$scripts = $cont.find("script");

Now you have extracted the scripts you can continue with your caching the same way you were doing but handling separately the html and the scripts, and in the moment you want to retrieve some html in the on-page DOM, you also append the scripts to the DOM.

Hope it helps, and sorry for asking for clarification here and not in a comment but i don't have enough reputation.

EDIT (2013-01-22)

News in jQuery core funcionality on handling script tags parsing, implemented on jQuery 2.0 beta (jQuery 1.9 will be the last version continuing the old behavior) from the jQuery 2.0 upgrade guide

Loading and running scripts inside HTML content

Prior to 1.9, any HTML-accepting method (e.g., $(), .append(), or .wrap()) executed any scripts in the HTML and removed them from the document to prevent them from being executed again. This still broke in situations where a script might be removed and reinserted into the document using methods such as .wrap(). As of 1.9, scripts inserted into a document are executed, but left in the document and tagged as already executed so they won't be executed again even if they are removed and reinserted.

Despite this change, it is very poor practice to mix executable JavaScript into HTML markup; it has design, security, reliability, and performance implications. For example, external script tags included in HTML are fetched synchronously and then evaluated, which can take a significant amount of time. There is no interface to notify when or whether those scripts load, or to take corrective actions when there is an error.

Code that attempts to load a script by cloning an existing script tag and injecting that clone into the document will no longer work, because the cloned script tag has already been marked as executed. To load a new script, use jQuery.getScript() instead.

Áxel Costas Pena
  • 5,886
  • 6
  • 28
  • 59
  • The response i get is the whole html page again, but with a different pages worth of data elements. `data.settings.*` are all selectors. The script tags are all children/descendents of the elements that the selector matches. – George Duckett Sep 21 '12 at 11:10
  • Don't have time to test now, but this solution looks like it could work. – George Duckett Sep 21 '12 at 11:11
  • Ok, I understand it completely now, and it's very strange the scripts don't get executed at the time you add the html to the off-page DOM. Any way, I suppose the script extraction I suggested you will be the solution, but I also don't have enough time and it's tested only in chrome. So good luck facing IE ;) – Áxel Costas Pena Sep 21 '12 at 11:17
  • Only thing that might be interesting is that although i get multiple rows (with their own script) in one go, i'm adding them to the page individually. That might make it hard to ensure i execute the right script as i add an individual row. Champ's idea of replacing the script tag with something else might be workable, as it would remain a child of the row i'm adding, so when adding i'd just find my changed script tag and execute it. – George Duckett Sep 21 '12 at 11:19
  • I understand, so it might be something like that: `$cont.find(rowselector).map(function (i, $el) { return { rows: $el, scripts: $el.find("script") };})` – Áxel Costas Pena Sep 21 '12 at 11:35
  • This has done the trick. The main point is to manipulate the .innerHTML property to preserve script tags until insertion into the page. – George Duckett Sep 24 '12 at 10:12
1

As I understood the problem is that dynamically loaded scripts aren't being executed. I have found the following post on SO and according to solution <script> tags added to existing DOM elements aren't being executed you have to create a new element and append script to it, then add that element to whereever you want.

Community
  • 1
  • 1
haynar
  • 5,961
  • 7
  • 33
  • 53
  • The answer in that post looks to be along the right lines, however I want to only add certain elements and don't know how to select them without using jquery (which strips the script tags). – George Duckett Sep 21 '12 at 11:04
  • ah ok, now I completely got the idea. I think you should use regular expressions like it was suggested to get the content of ` – haynar Sep 21 '12 at 11:09
0

Simply if you want to execute the script get the script content and then use eval(not recommended) or create a script tag by jQuery and insert the script in the tag it will automatically get executed

var nextPageDOM = $('<div/>');
var jqxhr = $.get(data.nextPageURL, function(nextPageHTML) {
    nextPageHTML =  stripScripts(nextPageHTML );  // remove the script tag ;)
    nextPageDOM.html($(nextPageHTML).find(data.settings.pagedContent));

    // Get the elements to add to the page
    data.nextPageElements = nextPageDOM.find(data.settings.rowSelector);
}

This function will get all the script from the html string

function getScripts(text) {
       var SCRIPT_REGEX = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi;
         text = text.match(SCRIPT_REGEX);
        return test;   // you can use eval to execute the script or you can crete a script tag and insert the script in the tag and it will get executed
      }

Demo script in the page

`<scr<script>Ha!</script>ipt> alert(document.cookie);</script>`

this function will remove all of them

   function stripScripts(text) {
       var SCRIPT_REGEX = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi;
       while (SCRIPT_REGEX.test(text)) {
         text = text.replace(SCRIPT_REGEX, "");
        }
      return test
      }
GajendraSinghParihar
  • 9,051
  • 11
  • 36
  • 64
  • I **do** want to execute the script, the problem is when i add it to the off page dom jquery strips the script out, so it never gets executed. – George Duckett Sep 21 '12 at 11:05
  • then just match inserted of replace wait i give u a function for that – GajendraSinghParihar Sep 21 '12 at 11:14
  • Using your `getScripts` function i can get scripts for every row returned, however, how can I execute the correct script when inserting rows individually? I.e. how do I know which script tag goes with which row element? – George Duckett Sep 21 '12 at 11:28
  • can you please give a demo of your `nextPageDOM` how will it look like and how script tag will present in each row ? – GajendraSinghParihar Sep 21 '12 at 11:34
0

What about using $.ajax({... datatype:script ...}) instead of $.get(...) in that way you can execute all needed js, and of course inside this pice of js, the code to append the visible parts of the page.

On page side:

$.ajax({url:data.nextPageURL, datatype:script, data:relevant_data_map,});

On server response (please notice that tags must not be present):

alert('this is running');                        // or whatever
var nextPageDOM = $('<div/>');
nextPageDOM.html('this is from the server');     // or whatever
$(nextPageDOM).insertBefore("#end_of_content_mark");
Saic Siquot
  • 6,513
  • 5
  • 34
  • 56