JavaScript - replace document.write() with DOM function - javascript

I am looking for some kind of a custom generic function that would allow me to dynamically create elements with JavaScript/jQuery. Since I am working with XHTML and the application/xhtml+xml MIME type, I am forced to work with DOM functions instead of document.write();. Take the following fallback code for jQuery:
if (!window.jQuery) {
if (!navigator.userAgent.toLowerCase().match(/msie\s[5-8]/i)) {
document.write('<script charset="UTF-8" type="application/javascript" src="/assets/scripts/jquery-2.min.js"><\/script>');
} else {
document.write('<script charset="UTF-8" type="text/javascript" src="/assets/scripts/jquery-1.min.js"><\/script>');
}
}
The browser console will display an error and the code will not be executed. So what I think I need is something like this:
function writeXHTML(tag) {
// some ugly DOM-based code to execute in order for the tag to be created. Support IE, please!
}
And then use it like this:
if (!window.jQuery) {
if (!navigator.userAgent.toLowerCase().match(/msie\s[5-8]/i)) {
writeXHTML('<script charset="UTF-8" type="application/javascript" src="/assets/scripts/jquery-2.min.js"><\/script>');
} else {
writeXHTML('<script charset="UTF-8" type="text/javascript" src="/assets/scripts/jquery-1.min.js"><\/script>');
}
}
Obviously, I will need to create the jQuery script in the <head>, before the other <script>s that depend on it. And I would also need this to be done from an external script since I don't like inlin-ing :P
I guess I could use ids to do that, but what code should the writeXHTML(); function have? Thanks in advance!

Related

run internal script after external script has been loaded via defer

It is well known to everyone that using defer is an efficient way to minimize the loading time of a website.
In my project, I am using Vue (included in the header using defer) and in a circumstance, I want to use a component that is created by another person. When I try to do Vue.Component(...) in the body of the HTML, it says Vue is undefined. It seems that my script in the body is running before the external script has been loaded. Is there any way to fix this issue?
I tried to do document.onload, but the function itself is not working.
PS: Just to be clear, the external script is referring to my js file where Vue is defined and I am not talking about the third party library at all.
Instead of document.onload you need to use window.onload or document.body.onload.
But an even better way is to wait for the load event on <script> tag:
<html>
<head>
<script id="vue-script" src="vue.js" charset="utf-8" defer></script>
</head>
<body>
<script type="text/javascript">
function onVueLoaded() {
Vue.render();
}
if ('Vue' in window) {
onVueLoaded();
} else {
var script = document.getElementById('vue-script');
script.addEventListener('load', onVueLoaded);
script.addEventListener('error', () => console.warn('failed to load Vue.js'));
}
</script>
</body>
</html>
Here I also added a handler for the error event if you wanted to explicitly handle loading errors.

How to defer inline Javascript?

I have the following html code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/blazy/1.8.2/blazy.min.js" defer></script>
<script src="https://code.jquery.com/jquery-2.1.4.min.js" integrity="sha256-8WqyJLuWKRBVhxXIL1jBDD7SDxU936oZkCnxQbWwJVw=" crossorigin="anonymous" defer></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lightbox2/2.9.0/js/lightbox.min.js" defer></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous" defer></script>
<!-- 26 dec flexslider js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/flexslider/2.6.3/jquery.flexslider.min.js" defer></script>
<script defer>
(function($) {
$(document).ready(function() {
//do something with b-lazy plugin, lightbox plugin and then with flexslider
});
})(jQuery);
</script>
</head>
<body>
</body>
</html>
I get an error, saying jQuery is not defined. Now even if I remove defer from my inline JS code, it says jQuery is undefined. For some reason I have to keep the jQuery plugins in the head and keep my JS code inline. My question is:
Why doesn't inline Javascript code get deferred when defer attribute is present on it?
Is there a way to imitate the defer behavior on my inline Javascript code? I can put that at the end of body tag if required.
The scripts with the defer attribute load in the order they are specified, but not before the document itself has been loaded. As defer has no effect on script tags unless they also have the src attribute, the first script that gets executed is your inline script. So at that time jQuery is not loaded yet.
You can solve this in at least two ways:
Put your inline script in a .js file and reference it with a src attribute (in addition to the defer attribute which you already had there), or
Let your inline script wait for the document and the deferred scripts to be loaded. The DOMContentLoaded event will fire when that has happened:
<script>
window.addEventListener('DOMContentLoaded', function() {
(function($) {
//do something with b-lazy plugin, lightbox plugin and then with flexslider
})(jQuery);
});
</script>
NB: Notice that in the latter case $(document).ready(function() is not included any more, as that would wait for the same event (DOMContentLoaded). You could still include it like you had in your original code, but then jQuery would just execute the callback immediately, which makes no practical difference.
You can create a Base64 URL out of the script and put it into the src!
<script src="data:text/javascript;base64,YWxlcnQoJ0hlbGxvIHdvcmxkIScpOw=="
defer>
</script>
I built a quick test to see it in action.
You should see an alert with Hello world! last if defer is working:
<script defer>
alert('Why no defer?!?');
</script>
<!-- alert('Hello world!'); -->
<script src="data:text/javascript;base64,YWxlcnQoJ0hlbGxvIHdvcmxkIScpOw=="
defer></script>
<script>
alert('Buh-bye world!');
</script>
Doing it manually is a little laborious so if you have the luxury of compiling your HTML in some way (Handlebars, Angular, etc.) then that helps a lot.
I'm currently using:
<script src="data:text/javascript;base64,{{base64 "alert('Hello world!');"}}"
defer>
</script>
You can also use type="module":
<meta charset="utf-8">
<script type="module">
let t = document.getElementById('top');
console.log(t);
</script>
<h1 id="top">Top Questions</h1>
https://developer.mozilla.org/docs/Web/HTML/Element/script#attr-type
From MDN docs:
defer
This Boolean attribute is set to indicate to a browser that the script is meant to be executed after the document has been parsed, but before firing DOMContentLoaded. The defer attribute should only be used on external scripts.
This is called an IIFE (Immediately Invoked Function Expression) which gets executed before DOM is available. So, in that case jQuery is undefined because it it not in the DOM.
defer loading with plain text Data URI - Chrome and FF
#noLib #vanillaJS
suggest not to use on Cross Browser PRODuction
until MS IE dies and MS Edge will adopt the Chromium open source ;)
the only way to defer script is external file or Data_URI (without using event DOMContentLoaded)
defer
spec script#attr-defer (MDN web docs): "This attribute must not be used if the src attribute is absent (i.e. for inline scripts), in this case it would have no effect.)"
Data_URI
spec Data_URI
with right type "text/javascript" there is no need to base64 at all... ;)
using plain text so you can use simple:
<script defer src="data:text/javascript,
//do something with b-lazy plugin, lightbox plugin and then with flexslider
lightbox.option({
resizeDuration: 200,
wrapAround: true
})
">
yes, it's little bit weird hack, but <script type="module"> are deferred by default, there is no other option to mix following in exact order:
module external files - deferred by default
module inline scripts - deferred by default
external files - optionally deferred
inline scripts - only with this hack - as I know (without libraries/frameworks)
Defer/async script tags are not good enough
There is a common knowledge that you should use <script src=".." async defer> (or set script.async = true before assigning src, when you do it from JS) and/or put your scripts at the very bottom of the page, so that as much as possible of the page gets loaded and rendered to the user, as fast as possible.
defer.js (note: I am the author of this script) is written in plain JavaScript, making lazy-loading other contents more fast and performant. You can defer any javascript files as well as inline script blocks efficiently.
If your page is just an HTML page enhanced with some JavaScript, then you're good with just <script async>. It takes time for browser to parse and execute those scripts, and each UI change may reflow your layout, make your load speed more slow, no one likes staring at a blank white page; users are impatient and will leave quickly.
In various cases, using async or defer does not deliver faster page speed than defer.js does.
I checked all the proposed solutions but all have their disadvantages. So I invented my own.
Put this inline script into your head tag or right after the start of body tag:
<script>var Defer = []; document.addEventListener('DOMContentLoaded', function() { while (Defer.length) Defer.shift().call(); }); </script>
This one liner will collect all the inline scripts you want to defer and run them respectively as soon as document is fully loaded. Now anytime you need to run an inline script deferred, just register it like:
<script>
alert('This alert will show immediately.');
Defer.push(function() {
alert('This alert will show only after document is loaded.');
// You can use anything which is not loaded yet, like jQuery
$(".selector").doSomeJqueryStuff();
});
// You can use it as many times as you like and in any place of your DOM.
Defer.push(function() {
// Any inline code you want to defer
});
</script>
This inline script will run only after document is loaded. That means you can run inline jQuery script having your jQuery stay at the end of your DOM.
You can use this data url as src attribute
data:application/javascript,eval(document.currentScript.textContent)
which takes this current script tag and evaluate its content as if it was inside an external file.
it also works with lazy attribute.
it uses document.currentScript which not supported by IE browsers.
<script defer src="https://cdn.jsdelivr.net/npm/vue"></script>
<script defer src="data:application/javascript,eval(document.currentScript.textContent)">
console.log('defered', typeof Vue); // function
</script>
<script>
console.log('not defered', typeof Vue); // undefined
</script>
There is a somewhat less obscure way to accomplish deferral that does not require callbacks, promises, or data urls ... although it does a little DOM manipulation in the background. The tiny library (109 bytes compressed/gziped) https://www.npmjs.com/package/deferscript let's you do this. The example below is based on the original post.
<script src="https://cdnjs.cloudflare.com/ajax/libs/flexslider/2.6.3/jquery.flexslider.min.js" defer>
</script>
<script src="./deferscript.js" defer>
(function($) {
$(document).ready(function() {
//do something with b-lazy plugin, lightbox plugin and then with flexslider
});
})(jQuery);
</script>
All you have to do is insert a src attribute with the value ./deferscript.js.
If the problem is that jQuery variable $ is not defined, maybe you can create a fake $ function that returns a ready function waiting for the DOMContentLoaded?
All my inline scripts has $(document).ready(..... and the problem is that $ is not defined as the header scripts are deferred.
So, just add a fake $ in an inline script in head:
<script type="text/javascript">
var $ = function(element) {
return {
ready: function(callback) {
// in case the document is already rendered
if (document.readyState!="loading") callback();
// modern browsers
else if (document.addEventListener)
document.addEventListener("DOMContentLoaded", callback);
// IE <= 8
else document.attachEvent("onreadystatechange", function(){
if (document.readyState=="complete") callback();
});
}
};
};
</script>

Conflict between jQuery.load(), jQuery.focus(), and link tags

Back-story
I am creating a web application in which individual pages are "loaded" via the jQuery .load function. Originally the loaded page was a single file, but as it got longer I decided to split it into a .html file, a .css file, and a .js file.
Strangely, a single design flaw arose surrounding an element that was positioned using percentage values within the css. To see if I modified the styling while I moved, I replace the style tag (omitting the link tag instead) and it worked fine. Back and fourth a few times and I learned it was strictly occurring only when I used link tags rather than embedding it via style tags.
I wanted to use link tags, so I tried to narrow the problem down. After a while of fiddling, I traced it down to the .js file, specifically a usage of the .focus function on $(document).ready. If I comment out the .focus, everything works fine. Uncomment, and it breaks.
This appears to only happen in Chrome. It doesn't occur in FF26 or IE11.
Example
A fiddle.
Note that the problem only occurs in Chrome and that caching must be disabled. As Chrome's temporary cache disable doesn't extend into iframes of iframes, a direct result is easier to work with.
jQuery provides a callback function when the .load() method has completed and the DOM has been updated. This is where you would want to operate on elements that have been inserted from your external url.
<script>
$(document).ready(function() {
$('#content').load('html/portal.html', function() {
//now i can operate on elements loaded from html/portal.html
//they have been inserted into the DOM
$('input#input').focus();
})
});
</script>
As far as references to the link element, if you want to dynamically load a stylesheet from an external url using javascript you can employ a javascript function something like this:
//load deferred stylesheets
function loadStyleSheet(src) {
if (document.createStyleSheet) {
document.createStyleSheet(src);
} else {
$("head").append($("<link rel='stylesheet' href='"+src+"' type='text/css' media='screen' />"));
}
}
If you want to load an external javascript file you can use jQuery.getScript():
jQuery.getScript('/js/external.js');
So putting all of this together, if you want to load some content from an external resource, insert it into the DOM and the load an external stylesheet and an external javascript resource you could do so as follows:
<!DOCTYPE html>
<html>
<head>
<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
<script>
$(document).ready(function() {
//function to load a deferred stylesheets
var loadStyleSheet = function(src) {
if (document.createStyleSheet) {
document.createStyleSheet(src);
} else {
$("head").append($("<link rel='stylesheet' href='"+src+"' type='text/css' media='screen' />"));
}
}
$('#someWrapper').load('external-url.html #someWrapper > *', function() {
loadStyleSheet('/css/external.css');
jQuery.getScript('/js/external.js');
})
});
</script>
</head>
<body>
<div id="someWrapper">
<!-- external content is loaded here -->
</div>
</body>
</html>

JSONRequest.get unresponsive

I am trying to send get requests to the Google Places API with this code:
<script type="text/javascript" src="json2.js">
var googleQuery;
function load() {
googleQuery = JSONRequest.get(
"https://maps.googleapis.com/maps/api/place/details/json?reference=3af0d044d45cd8587d9a3522bc98a95d4f60c6a8&sensor=true&key=xxxxxxxxxxxxxxxx",
function (googleQuery, value, exception) {
if (value) {
processResponse(value);
}
else {
processError(exception);
}
}
);
}
</script>
And calling the load function in the body onload.
<body onload="load()">
</body>
I am including the src="json2.js" in this <script> instead of in its own <script>, since I was getting a "JSONRequest is undefined" error...but I am still getting a strange "load is undefined" error.
Am I going about this JSON request correctly?
Try:
<script type="text/javascript" src="json2.js"></script>
<script type="text/javascript">
var googleQuery;
function load(){
googleQuery = JSONRequest.get(
"https://maps.googleapis.com/maps/api/place/details/json?reference=3af0d044d45cd8587d9a3522bc98a95d4f60c6a8&sensor=true&key=xxxxxxxxxxxxxxxx",
function (googleQuery, value, exception) {
if (value) {
processResponse(value);
} else {
processError(exception);
}
}
);
}
</script>
You can't have JavaScript code inside a script tag which has the src attribute. You should place the inline code on another script tag, otherwise it won't be executed.
Replace the line :
<script type="text/javascript" src="json2.js">
with
<script type="text/javascript" src="json2.js"></script>
<script>
Your JS code is being ignored since you specified an src attribute.
JSONRequest is more so just a proposal which browsers can implement at their will (I think Firefox does).
I'm not sure if there are any libraries that can be script sourced to in order to use JSONRequest, but an alternative is to use flyJSONP (http://alotaiba.github.com/FlyJSONP/#!/demo).
flyJSONP uses YQL (Yahoo Query Language) to do any cross domain post/get, and I highly recommend it (especially for google api's such as oAuth 2.0 and ClientLogin)... and it has a debugger mechanism.
Also, there is jankyPost (http://saunter.org/janky.post/). I have not used it but I'm sure I will and I like its concept. Its kinda clugy, or well... janky... but read about how it works (short paragraph) and you'll love it and want to build you own perhaps.
--Cody

XHTML Strict is not valid because of html tags inside Javascript

I'm creating a site using XHTML Strict markup. Inside the html I need to put a js script:
<script type="text/javascript">
$(document).ready(function () { $('#nav li#nav-strona-glowna').append('<span>Strona glowna</span>');
</script>
Unfortunately the xhtml is no longer valid, because of an "" tag inside this script. How can I validate xhtml without removing it?
You want a CDATA tag. The example stolen from that MDC page:
<script type="text/javascript">
//<![CDATA[
var i = 0;
while (++i < 10)
{
// ...
}
//]]>
</script>
In general it's better to avoid the issue by having your JS (and CSS) in external files.
Try:
<script type="text/javascript">
<![CDATA[
$(document).ready(function () { $('#nav li#nav-strona-glowna').append('<span>Strona glowna</span>');
]]>
</script>
<script type="text/javascript"><![CDATA[
// your code
]]></script>
Should fix your problem
What error does validator gives you exactly? The code you posted looks pretty valid.
Btw. If you don't need XHTML for some very specific reason,
(http://xhtml.com/en/xhtml/serving-xhtml-as-xml/)
you're probably better off with just HTML4 or HTML5, which you can also make valid code with.
CDATA Wrapper
<script>
//<![CDATA[
// js goes here - bypasses browser HTML parser
//]]>
</script>
I suggest that you find ways to inject HTML in your page that doesn't directly involve writing HTML markup in your JavaScript. Your relationship with your web designer will be much stronger, more cooperative, and fruitful if you keep your content, presentation, and behavior separated.
But for those cases where that is not possible, a CDATA wrapper will prevent the browser from parsing your JavaScript as HTML.
Keep in mind that there is a reason they call it HTML Strict.
Update
There's a problem in your code, which is that it's missing a close brace and parenthesis. Possibly a typo?
Regarding the XHTML validation, the best option is to put you JavaScript in a separate file. Failing that, add a CDATA section around your script:
<script type="text/javascript">
//<![CDATA[
$(document).ready(function () { $('#nav li#nav-strona-glowna').append('<span>Strona glowna</span>'); });
//]]>
</script>
Note that the JavaScript comments are there for browsers such as IE which do not parse XHTML as XML. See https://developer.mozilla.org/en/properly_using_css_and_javascript_in_xhtml_documents

Categories

Resources