Dynamically load React Component in JS file - javascript

I have a couple of react files which contain react components, e.g.:
class MyComponent extends React.Component {
...
}
Currently, I import them using the script elements, e.g.:
<script type="text/babel" src="http://127.0.0.1:3000/myComponent.js"> </script>
I want to dynamically load them from a single js file. Therefore I was following the idea presented here:
var script = document.createElement('script');
script.type='text/babel';
script.onload = function () {
console.log('done');
resolve();
};
script.src = "http://127.0.0.1:3000/myComponent.js";
document.head.appendChild(script);
However, onload is never called and I do not get any error message in Chrome. If I change type to "text/javascript" the onload method is called but I get a syntax error: "<" is a undefined token....
I know that I can compile the bable files to js but I dont want to that during development...

Try this alternate code, see if this works for you. Apparently you need babel script before text/babel can be identified as valid.
Look at In browser here: https://babeljs.io/en/setup#installation
var script = document.createElement("script");
script.type = 'text/babel';
script.addEventListener("load", function(event) {
console.log("Script finished loading and executing");
});
script.src = "https://code.jquery.com/jquery-3.3.1.min.js";
document.getElementsByTagName("script")[0].parentNode.appendChild(script);
<script src="https://unpkg.com/#babel/standalone/babel.min.js"></script>
<div id="output"></div>
<script type="text/babel">
const getMessage = () => "Hello World";
document.getElementById('output').innerHTML = getMessage();
</script>

Related

React, how to add external script and make its functions available?

I wonder how I would add an external script to an React application and make its functions available. Let's take this script https://www.cssscript.com/confetti-falling-animation/
In a "not-react-application" I would add it like this in my DOM
<script src="confetti.js"></script>
and then call it's functions like this
startConfetti();
or
stopConfetti();
However, this does not work in React. I know that I can add a <script /> tag like this:
useEffect(() => {
const script = document.createElement('script');
script.src = './confetti.js';
script.async = true;
document.body.appendChild(script);
return () => {
document.body.removeChild(script);
}
}, []);
But this does not make the functions startConfetti() or stopConfetti() available. They are undefined.
How would I add the script and its functionalities in a React App?
Add the script in index.html's head tag, like so (with the correct path to the JavaScript file):
<script src="confetti.js"></script>
Then in your React component, you could get it with the help of window object, this way:
const startConfetti = window.startConfetti;
startConfetti();

How to test dynamic changes to the DOM via JavaScript with Jest or some other testing library?

I am dynamically adding a <script> tag to the document <head> on page load based on the environment.
The Function:
export const loadScript = () => {
// load script tag into head
const HEAD = document.getElementsByTagName('head')
const SCRIPT_TAG = document.createElement('script')
SCRIPT_TAG.setAttribute('src', process.env.SCRIPT_SRC)
SCRIPT_TAG.setAttribute('async', true)
HEAD[0].append(SCRIPT_TAG)
}
I want to write a test that checks if once the loadScript() function is run that the <script> tag made it into the head. Our environment is set up with Jest, and I haven't found a satisfactory example that demonstrates how to do it, or works.
I am new to testing, and would appreciate any solutions, or hints offered.
I suppose the easiest way to test it would be something like this:
test('loadScript', () => {
process.env.SCRIPT_SRC = 'the-src';
loadScript();
expect(document.head.innerHTML).toBe('<script src="the-src" async="true"></script>');
});
This works because the default test environment for Jest is jsdom which simulates the document.
test('loadScript', () => {
loadScript();
const script = document.getElementsByTagName('script')[0];
// trigger the callback
script.onreadystatechange(); // or script.onLoad();
expect("something which you have on load").toBe('expected result on load');
});

Conditionally load JavaScript file

I need a JS statement that determine which JavaScript file to use.
I have one file:
<script type="text/javascript" src="js/jquery_computer.js"></script>
But when the screen width is less than 500px, I want load another file instead:
<script type="text/javascript" src="js/mobile_version.js"></script>
I have tried everything and it is not working.
You'd have to create that markup yourself in JS. Something like this:
var head = document.getElementsByTagName('head')[0];
var js = document.createElement("script");
js.type = "text/javascript";
if (screen.width > 500)
{
js.src = "js/jquery_computer.js";
}
else
{
js.src = "js/mobile_version.js";
}
head.appendChild(js);
If you want the script loaded asynchronously, the other answers here do that.
If you want it loaded synchronously with page load, this is one of the very, very few remaining valid uses cases for document.write:
<script>
(function() { // Scoping function to avoid globals
var src = /*you want the main version*/ ? "jquery_computer.js" : "mobile_version.js";
document.write('<script src="js/' + src + '"><\/script>');
})();
</script>
(I've removed type because JavaScript is the default, specifying it isn't useful.)
Maybe you can use matchMedia.js and can load a script using jQuery.getScript
$(function(){
if (matchMedia('only screen and (max-width: 500px)').matches) {
$.getScript(...);
}
});
Best would be to use built-in matchMedia API.
var script = document.createElement('script');
script.type='text/javascript';
if(window.matchMedia("(min-width:500px)").matches) {
script.src = 'js/jquery.slitslider.js';
}else{
script.src = 'js/mobile_version.js';
}
document.getElementsByTagName('head')[0].appendChild(script);
Drawback is that it is not supported in IE < 10
You don't need jQuery for this, it suffices to create the <script> tag in the DOM dynamically:
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.type = 'text/javascript';
if (<screen-width less than 500>)
script.src = "js/mobile_version.js";
else
script.src = "js/jquery_computer.js";
head.appendChild(script);
$(function(){
var width = $(document).width(),
mobile = 500;
if (width > mobile) {
$('head').append('<script class="desktop" type="text/javascript" src="js/jquery_computer.js"></script>');
$('head').find('.mobile').remove();
}
else
{
$('head').append('<script class="mobile" type="text/javascript" src="js/mobile_version.js"></script>');
$('head').find('.desktop').remove();
}
});
just use if else to detect condition and use class on script element
may be it help
You could use the async import() if you like.
import(condition ? 'js/desktop_version.js' : 'js/mobile_version.js')
In a script tag with <script type="module"> or inside a module loaded with import() from a regular <script> you can also use top-level await in the newest (experimental) browsers
<script type="module">
await import(condition ? 'js/desktop_version.js' : 'js/mobile_version.js')
</script>
But it won't work for every script.
A common reason why is those UMD module bundler that adds a closure (function(global){...}(this)) since this is undefined in modules.
Third party scripts needs to have CORS enable to load
Won't work in IE, but it's dead anyway
See other known differences
you can use $.getScript in jQuery
see here for details

Is it possible to execute Javascript code that was retrieved using pjax?

Let's say I have some simple Javascript like:
<script>
var hello = function(){
alert("Hello World!");
}
</script>
.. on a page helloworld.html. If I loaded this script block into another page using Pjax. How do I execute the function hello()?
For security reasons, many browsers will not run Javascript injected by innerHTML, which I'm thinking Pjax likely uses. (Here's a minimal example.)
Maybe the solution proposed in Pjax's issue #48 will help
What worked for me was to place my jQuery code in a function, call it
normally on document.ready (for non-pushState browsers), and then bind
the function to pjax:end, i.e.:
$('body').bind 'pjax:end', mainFunction
This is possible with PJAX. You just need to have the script tag with type text/javascript.
Code from PJAX library:
function executeScriptTags(scripts) {
if (!scripts) return
var existingScripts = $('script[src]')
scripts.each(function() {
var src = this.src
var matchedScripts = existingScripts.filter(function() {
return this.src === src
})
if (matchedScripts.length) {
matchedScripts.remove();
}
console.error("FOUND SCRIPTS", scripts, matchedScripts.length);
var script = document.createElement('script')
script.type = $(this).attr('type')
script.src = $(this).attr('src')
document.head.appendChild(script)
})
}

How to load JS scripts that will add only one additional script

I have 2 script files that you put in the <head>
However, they share the same Util script file.
I want that in case they are both loaded on the same page, the util file will load only once.
If only one of them is loaded, the util will still be loaded once...
I cant use <script src=...utils.js>... only the 2 scripts
I am using
var s = document.createElement("script");
s.src = s3 + "/js_enc/utils.js";
s.type = "text/javascript";
document.getElementsByTagName("head")[0].appendChild(s);
What is the best way to achieve this?
thanks
This doesn't seem like a problem, since the Util script will only be loaded when it is declared:
<script type='text/javascript' src='util.js'></script>
<script type='text/javascript' src='script1.js'></script>
<script type='text/javascript' src='script2.js'></script>
But if you want to check that the script has been loaded, you could set a global variable in the Util script, something like
var utilFound = true;
Then check in each script to see if it is set or not.
if(!utilFound) { alert("Util not loaded!"); }
You could also toggle the variable depending on some condition, for example:
if(utilFound) {
alert("Util hasn't been accessed yet.");
utilFound = false;
}
else {
alert("Util has already been accessed.");
}
UPDATE
As per your edit, the "check if set and toggle" solution would work fine. If the variable has been set, don't run your document.createElement code.
In your util.js file, add
var utilLoaded = false;
Then in each script, add your snippet, plus a utilLoaded check/toggle:
if(!utilLoaded) {
var s = document.createElement("script");
....
utilLoaded = true;
}

Categories