suppose you have a simple web page that dynamically loads content that looks like this:
- main.html -
<!DOCTYPE html>
<html xmlns="https://www.w3.org/1999/xhtml">
<head>
<script type="text/javascript"
src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-1.6.2.js">
</script>
<script type="text/javascript">
$(function() {
$.ajax({
type: 'get', cache: false, url: '/svc.html',
success: function(h) {
$('#main').html(h);
}
});
});
</script>
</head>
<body>
<div id='main'>
loading...
</div>
</body>
</html>
and that the page it loads uses a little Javascript in a separate file:
- svc.html -
<script type="text/javascript"
src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-1.6.2.js">
</script>
<script type="text/javascript" src="/plugin.js" css="/plugin.css"></script>
<div id='inner'>
dynamically loaded content
</div>
notice the css attribute on the script tag - it indicates a style-sheet that belongs to the script and which the script will load for us. Here's the script:
- plugin.js -
var css = $('<link />', {
rel: "stylesheet", type: "text/css", href: $('script:last').attr('css')
});
$('head').append(css);
and, finally, the style-sheet, which merely colours the inner div that gets loaded, as proof that it all works:
- plugin.css -
#inner
{
border: solid 1px blue;
}
now, you'll notice the way the style-sheet gets loaded: we look at $('script') and pick off the last one, then we grab the css attribute and attach a link item to the head of the document.
I can visit /svc.html and the javascript runs, loads my style-sheet, all's cool - however, if I visit /main.html, the javascript runs but fails to find the loading of the plugin.js in the array of $('script') (and therefore fails to load the stylesheet). Note that the plugin script does run, it just doesn't see itself.
Is this a bug? a limitation of the jQuery AJAX method? shouldn't $('script') reflect all scripts that have been loaded?
* edit *
after much wailing and gnashing of teeth, I decided to go for Kevin B's solution, however, I can't get it to work, basically because the plugin.js code runs at the time the scriptNode gets inserted, but within the plugin code, the node has not yet been inserted and is thus not available in the $('script') array - so I'm still SOL. I've put all the code for review here:
http://www.arix.com/tmp/loadWithJs/
The jQuery code that adds HTML to the DOM always strips out <script> tags. It runs them and then throws them away.
An exception to that behavior is when you use "$.load()" with the hack that allows you to load a fragment of a page:
$.load("http://something.com/whatever #stuff_I_want", function() { ... });
In that case, the scripts are stripped and not evaluated/run.
you can use $.ajax to get the html, strip out the script tags yourself, append the content, and then append the script tag to the location in the dom where you want it so that it executes the way you want it to.
$.ajax({...}).done(function(html){
var htmlToAppend = html;
// replace all script tags with divs
htmlToAppend.replace(/<script/ig,"<div class='script'");
htmlToAppend.replace(/<\/script>/ig,"</div>");
// detach script divs from html
htmlToAppend = $(htmlToAppend);
var scripts = htmlToAppend.find(".script").detach();
// append html to target div
htmlToAppend.appendTo("#target");
// loop through scripts and append them to the dom
scripts.each(function(){
var $script = $(this),
scriptText = $script.html(),
scriptNode = document.createElement('script');
$(scriptNode).attr('css', $script.attr('css');
scriptNode.appendChild(document.createTextNode(scriptText));
$("#target")[0].appendChild(scriptNode);
});
});
I haven't tested this code, but it is based on code from history.js
Edit: here's a simpler solution more tailored for your needs:
$("#target").load(url,function(obj){
// get css attribute from script tag using the raw returned html
var cssPath = obj.responseText.match(/css="(.*)"/i)[1];
$('<link />', {
rel: "stylesheet", type: "text/css", href: cssPath
}).appendTo('head');
});
since .load() strips the script tags and then executes them, this will read the raw response text and get the css path from it, then add the link element.
I'm not entirely sure why it is handled this way.
Edit: see comment.
$.ajax({...}).done(function(html){
$("#target")[0].innerHTML = html;
});
My solution would be rather simple
Why not load the the files needed in main, on the first load all together? Then simply have a listener event. (Check if the content is simply loaded?)
The only solution for loading on demand...
You simply has to AJAX call the script file itself...
<script type="text/javascript">
$(function () {
$.ajax({
type: 'get',
cache: false,
url: 'svc.html',
success: function(response) {
if (response) { // If any response aka, loaded the svc.html file...
$('#main').html(response);
$.ajax({
url: 'plugin.js',
dataType: 'script',
success: function(reponse) {
// You can now do something with the response here even...
}
});
}
}
});
});
</script>
My way
Make some type of loader instead on the actual page on load before displaying anything. (Plenty of tutorials with jQuery around the web for this.)
On of the problems here is that you CANNOT reload the jQuery in every page. Just the main page
should have the jQuery call:
<script type='text/javascript' src='jQuery-url'></script>
Or only one page can call it and ONLY THE CHILDREN (yes, the pages .LOADed from there) will be able to use jQuery.
I've ran into that a few times before I learned my lesson. This might not be the entire answer for your problems but could be the beginning of solving them.
ok, after many struggles, I've implemented Kevin B's solution into a plugin useful to others. Improvements welcome! (should I maybe put it in github?)
- loadWithJs.js -
// inspired by Kevin B's suggestions
// at: http://stackoverflow.com/questions/8234215/where-are-scripts-loaded-after-an-ajax-call
(function ($) {
$.fn.loadWithJs = function (o) {
var target = this;
var success = o.success;
o.success = function (html) {
// convert script tags
html = $('<div />').append(
html.replace(/<(\/)?script/ig, "<$1codex")
);
// detach script divs from html
var scripts = html.find("codex").detach();
// populate target
target.html(html.children());
// loop through scripts and append them to the dom
scripts.each(function () {
var script = $(this)[0];
var scriptNode = document.createElement('script');
for (var i = 0; i < script.attributes.length; i++) {
var attr = script.attributes[i];
$(scriptNode).attr(attr.name, attr.value);
}
scriptNode.appendChild(
document.createTextNode($(script).html())
);
target[0].appendChild(scriptNode);
});
if (success) success();
};
$.ajax(o);
};
})(jQuery);
then the caller can do:
$(function() {
$('#main').loadWithJs({
type: 'get', cache: false,
url: '/svc.html',
success: function() { console.log('loaded'); }
});
});
Related
What I'm trying to do seems simple: get an HTML page through $.ajax() and pull out a value from it.
$(function () {
$.ajax({
url: "/echo/html",
dataType: "html",
success: function (data) {
$('#data').text(data);
$('#wtf').html($(data).find('#link').text());
},
data: {
html: '<!DOCTYPE html><head><title><\/title><link href="../css/popup.css" rel="stylesheet" /><\/head><body><ul><li><a id="link">content<\/a><\/li><\/ul><\/body><\/html>'
}
});
});
The problem is that jQuery refuses to parse the returned HTML.
The fiddle I'm play with this in isn't working in the mean time, so there's little else I can do to provide a working example.
UPDATE: My new fiddle is working fine, but it seems the problem is that in my actual project I'm trying to parse a large, complex bit of HTML. Is this a known problem?
Your code works fine. You just aren't using jsFiddle's API correctly. Check the docs for /echo/html/ (http://doc.jsfiddle.net/use/echo.html#html):
URL: /echo/html/
Data has to be provided via POST
So, you need to update your AJAX call to use POST. Also the trailing slash is needed.
$(function () {
$.ajax({
url: "/echo/html/",
type: "post",
dataType: "html",
success: function (data) {
$('#data').text(data);
$('#wtf').html($(data).find('#link').text());
},
data: {
html: '<!DOCTYPE html><head><title><\/title><link href="../css/popup.css" rel="stylesheet" /><\/head><body><ul><li><a id="link">content<\/a><\/li><\/ul><\/body><\/html>'
}
});
});
DEMO: http://jsfiddle.net/hcrM8/6/
If you would like to parse it, jquery has a nifty trick :)
ParsedElements = $(htmlToParse);
Console.log(ParsedElements);
You now have DOM elements you can traverse without placing them in the body of the document.
jQuery.parseHTML()
http://api.jquery.com/jQuery.parseHTML/
str = "hello, <b>my name is</b> jQuery.",
html = $.parseHTML( str ),
nodeNames = [];
// Gather the parsed HTML's node names
$.each( html, function( i, el ) {
nodeNames[ i ] = "<li>" + el.nodeName + "</li>";
});
Some thing is wrong with your ajax on fiddle
http://jsfiddle.net/hcrM8/5/
var html= '<!DOCTYPE html><head><title><\/title><link href="../css/popup.css" rel="stylesheet" /><\/head><body><ul><li><a class="disabled" id="link">content<\/a><\/li><\/ul><\/body><\/html>';
h = $.parseHTML(html);
$('#data').text(h);
$('#wtf').html($(h).find('#link').text());
Why don't you just use the load method?
$( "#wtf" ).load( "/echo/html #link" );
Or, here's your fiddle fixed and working:
http://jsfiddle.net/hcrM8/4/
I had the same problem and i fixed encapsulating requested html code into just one container element.
Bad Example:
Linkname
<p>Hello world</p>
Jquery couldnt convert this to element, because it wishes to convert a single element tree. But those are not having a container. Following example should work:
Right Example:
<div>
Linkname
<p>Hello world</p>
</div>
All answers do not point to the real problem, jQuery seems to ignore the head and body tag and creates an array of nodes. What you normally want is, extract the body and parse it.
Take a look at this helpful answer, I do not want to copy his work: https://stackoverflow.com/a/12848798/2590616.
I am facing the same problem, and it is not because you are doing something wrong.
it's because the "link" tag is not supposed to have any innerHTML returned, it's explicitly excluded in jquery, you will find some where this line:
rnoInnerhtml = /<(?:script|style|link)/i,
This tag in HTML is supposed to link to external style sheet.
I am using phpbb3 as my board solution , i have already made the content pages etc .. all using integration with phpbb , now i want to add code from google adsense to my pages .. to do that i used the following script in overall_header.htm file in phpbb3/styles/prosilver/templates
<script type="text/javascript">
// <![CDATA[
$(document).ready(function(){
$("<!-- INCLUDE ad.html -->").insertAfter(".column1 > ul");
$("<!-- INCLUDE ad.html -->").insertAfter(".column1");
});
// ]]>
</script>
where i have the add code in ad.html , and the selectors are such to put the code into proper place.
but somehow phpbb reads this as XML , and instead of executing the script , it show it in text.. and also execute the "" part and show the ads where i placed the script.
http://tutorialfrenzy.com/how-to-block-a-website.php
you can see it there.
i tried using
<script type="text/javascript">
// Use any event to append the code
$(document).ready(function()
{
var s = document.createElement("script");
s.type = "text/javascript";
s.src = "http://scriptlocation/das.js";
// Use any selector
$("head").append(s);
});
it didn't helped .. Any suggestions ?
EDIT:
Also i used jquery getscript method
<script type="text/javascript">
$.getScript("chitikaad.js", function(){
alert("Running test.js");
});
</script>
As you pointed out, the includes don't work in the js file. Try changing it to this instead...
$(document).ready(function(){
$.ajax({
url: "ad.html",
success: function(data) {
$(data).insertAfter(".column1 > ul");
$(data).insertAfter(".column1");
}
});
});
You don't need <script> tags when using $.getScript(), and document.ready() may not be needed, but it does no harm and since I haven't seen your whole project it's better to have it than not have it (it does run the code, even if the document had already loaded previously.)
Incidentally, if you can add script to the main page, then you don't need $.getScript(). Just include the above script in the main page and it will load ad.html for you. Obviously if there's any other code in chitikaad.js then carry on loading it as you are.
i'm trying to load .aspx page using jquery like this;
$('ul#menu li ul.collapse').on('click', 'li a', function (e) {
e.preventDefault();
var div = $('#content .container-fluid .row-fluid .span12');
var url = $(this).attr('href');
setTimeout(function () {
var htmlPage = div.load(url + ' #MainContentHolder',
function () {
//other stuff
});
}, 1000);
});
However, my .aspx page does have javascript embedded like this;
<div id="MainContentHolder">
<h1>Test Aspx Content</h1>
<div>
<script type="text/javascript">
alert("hello");
</script>
</div>
</div
When, i see the resulted HTML, i can see the it is loaded (using firebug). But it's not showing the alert() message
I have also read all other questions on stackoverflow but it's not helpful on this to me;
UPDATE: As suggested in asnwer, following code only works for the first time. I tried to turn off the cache:false and also tried from GET to POST;
$.ajax({
url:url,
success:function(data){
div.html(data);
}
});
$('ul#menu li ul.collapse').on('click', 'li a', function (e) {
e.preventDefault();
var div = $('#content .container-fluid .row-fluid .span12');
var url = $(this).attr('href');
setTimeout(function () {
$.ajax({
url:url,
success:function(data){
div.html(data);
//other stuff
}
});
}, 1000);
});
var htmlPage = div.load(url + ' #MainContentHolder',
function () {
//other stuff
});
As per the documentation of .load(),
If .load() is called with a selector expression appended to the URL,
however, the scripts are stripped out prior to the DOM being updated,
and thus are not executed.
Instead you may want to take your script in js file and use $.getScript(), which will load a JavaScript file from the server using a GET HTTP request, then execute it.
Edit:
Alternate way is to have only script in your page and load that page as shown in below link
jQuery .load() call doesn't execute javascript in loaded html file
OR
As already suggested use $.ajax() and in success callback function, you need explicitly execute your JS code as shown in this post. Use eval.
OR
You can load your dynamic page using .load and in success callback function use $.getScript as already suggested earlier.
I hope it helps!
I would like to refire the styling and processing.js scripts that i linked to in the head so that they display correctly when brought in through an ajax-request. I see where in the ajax request this code needs to be, but i don't know how to tell the code to simply reapply the script. I've seen people using getScript() to do this, but from what i can tell this reloads the script, rather than simply telling it repeat or refire. Do all of the scripts need their own reinitialization? I found the syntax highlighters .highlight() method, but i am yet to get the processing script to load. currently, Processing.loadSketchFromSources($('#processing'), ['mysketch.pde']); does not work. I am using current versions of all libraries. Surprised i haven't been able to find the answer yet, as a lot of people seem to have the same problem. Thanks for your help!
index page:
$(document).ready(function () {
// put all your jQuery here.
//Check if url hash value exists (for bookmark)
$.history.init(pageload);
//highlight the selected link
$('a[href=' + document.location.hash + ']').addClass('selected');
//Search for link with REL set to ajax
$('a[rel=ajax]').live("click",function(){
//grab the full url
var hash = this.href;
//remove the # value
hash = hash.replace(/^.*#/, '');
//for back button
$.history.load(hash);
//clear the selected class and add the class class to the selected link
$('a[rel=ajax]').removeClass('selected');
$(this).addClass('selected');
//hide the content and show the progress bar
//$('#content').hide();
$('#loading').show();
//run the ajax
getPage();
//cancel the anchor tag behaviour
return false;
});
});
function pageload(hash) {
//if hash value exists, run the ajax
if (hash) getPage();
}
function getPage() {
//generate the parameter for the php script
var data = 'page=' + encodeURIComponent(document.location.hash);
$.ajax({
url: "loader.php",
type: "GET",
data: data,
cache: false,
success: function (html) {
//hide the progress bar
$('#loading').hide();
//add the content retrieved from ajax and put it in the #content div
$('#content').html(html);
//display the body with fadeIn transition
$('#content').fadeIn('fast');
//reapply styles?
//apply syntax highlighting. this works
SyntaxHighlighter.highlight();
//relaod processing sketch, currently displays nothing
Processing.loadSketchFromSources($('#processing'), ['mysketch.pde']);
}
});
}
This the ajax-loaded content:
<!--ajax'd content-->
<??>
<h2>code</h2>
<pre class="brush: php">
$last_modified = filemtime("header.php");
echo("last modified: ");
echo(date("m.j.y h:ia", $last_modified));
</pre>
<script type="application/processing">
</script>
<canvas data-processing-sources="mysketch.pde" id="processing">
</canvas>
</div>
</body>
</html>
<??>
So, let's analyze what usually happens when you include an (external or internal) Javascript code: It will automatically execute only the code that is available in the global scope. "Good" scripts will only add one command to the global scope which will then execute the initialization code somewhere in a function/method.
All you need to do is view the external Javascript file and find out what is being executed from the global scope. There is no general answer to that ... some scripts use an object and call its init() method ... but that is totally subject to the imagination of the developer.
If you have javascript that needs to trigger, you MUST add this to the head element:
var head = document.head || document.getElementsByTagName("head")[0];
var script = document.createElement("script");
script.innerHTML = "your AJAX-obtained js code";
head.appendChild(script);
The same trick goes for CSS. Add a element to the head with your CSS declarations as innerHTML. So: make sure to preprocess your AJAX response and split out the JavaScript and CSS elements, then add those to the document header. It's probably easier to make your response a JSON object along the lines of:
{
html: "<html>string<goes>here</goes></html>",
scripts: ["url1","url2","url2",...],
style: ...
}
and then parsing that JSON for the html (which you use as innerHTML for a new document.createElement("div") or something, and then append wherever it needs appending), the scripts (which you turn into elements for HEAD insertion) and the style declarations (which you turn into elements for HEAD insertion).
(On a functional note, your example AJAX response looks like it has PHP code in it. I have no idea what you're using it for, but that looks like a bad response)
Just incase anyone stumbles upon this:
If you have processing.js already loaded, simply call Processing.reload() in your AJAX success/complete function.
Perhaps you already have an element with id="processing" on your page. In that case $("#processing") will only return the first one. If that is the case, change the id or use a class instead.
The other option, which I don't recommend, is to use $("[id=processing]"). That will return every element on the page with id="processing". But, don't use it. Use unique ids in your page, or switch to using classes, whichever works best for you.
My site operates on an ajax "framework". Whenever a user opens a new page, that page loads in the necessary css and js files to view the page properly and adds them to the current document. The js files are loaded like this in jQuery:
function getJS(jsfiles) {
$.each(jsfiles, function(i, val) {
$.getScript(val);
});
}
My problem is that whenever a js-file is loaded, that file is added to the document permanently and operates across all ajax "sub-sites". Therefore I get problems with reserved function names, functions that requires certain DOM-elements to be present when executed and throws an error otherwise, etc. etc. Is there a way to remove these files from the document again?
I don't know how jQuery attaches new scripts, but you could do an AJAX request and build your own script elements in the callback, giving them a class that can be selected later for removal.
$.ajax({
url:'test.js',
dataType: 'text',
success: function(script) {
var head = document.getElementsByTagName('head')[0];
var scr_elem = document.createElement('script');
scr_elem.setAttribute("class","new_script");
scr_elem.setAttribute("className","new_script");
scr_elem.type="text/javascript";
scr_elem.appendChild(document.createTextNode(script));
head.appendChild(scr_elem);
}
});
// Later on
$('.new_script').remove();
Note that I've only tested this in Webkit and Firefox.
You could scope the contents of the JavaScript files you are loading, using a self-executing function. Or try assigning an ID to the script-blocks and remove them by doing: $("id of the script block").remove();.
You can try to do something like this
$('head').empty();
$('head').append('<script src="yourscript.js" type="text/javascript" />');
Hope this helps