How to handle page level pure javascript in react - javascript

First time using react and am converting a poc. Have a bunch of pure javascript, mostly code for animations on a canvas (almost game level). Each page has too much code to load it all in the base html file.
I'm sure there is something I'm missing in my searches, but I'm not sure the correct way to handle including it with each individual page.
I tried just putting it inline like this, but it doesn't like some of the code, so I'm assuming it's not the best way.
componentDidMount() { <script>....</script> }
Found an example doing
componentDidMount() {
const script = document.createElement("script");
script.src = "file.js";
script.async = true;
document.body.appendChild(script);
No matter how I path it, it just loads up the index page. So unexpected error < because it starts with < html>
Not sure it matters, but my JS includes a number of functions, var declarations, addEventListener and an onload.

You are looking for import.
import scriptFromGame from './script.js'
const App = ()=> {
// code reuse from script.js
//let's assume points
const {points} = scriptFromGame;
return <div> {points} </div>
}

Related

jquery append and run tag <script> VS js vanilla

I am updating my application and I have decided to use bootstrap 5 which no longer has the jquery dependency and to eliminate this dependence from my application as well.
So I'm rewriting the javscript part from jquery to js vanilla.
I use AJAX a lot to load content in different divs and for their subsequent manipulation based on certain events triggered by the user.
Unfortunately, in some cases I find myself forced to hang a script tag together with the html part to run a javascript function that I cannot execute in any other way.
For example:
<script>myFuncrion(val1, val2, val3);</script>
<div id="myDiv">
<div>Other html code ..</div>
</div>
With jquery something like this was enough to append the markup to a given div and execute the javascipt function
$('#elementToAppend').append(html);
Converting this to vanilla js I get something like this
document.getElementById('elementToAppend').innerHTML = html;
Everything is correctly appended, but obviously the js is not executed
I don't know jquery behind the scenes, but I assume it is being manipulated in some way for the javascript part to run.
I then created a function that would get everything back to working
const functionOne = (elem) => {
// I make the AJAX call...
let jsAndData = getJsAndData(data);
// Append the html string
document.querySelector(elem).innerHTML = jsAndData.cleanData;
// I run the js
eval(jsAndData.js);
}
const getJsAndData = data => {
var js = "";
// Check if there is a <script> tag
let script = data.match(/<script>(.*?)<\/script>/g);
// If there is a script tag I extrapolate the js content
if (script) {
script.map(val => {
js = val.replace(/<\/?script>/g, "");
});
// I remove the <script> tag and its content from the html string
let cleanData = data.replace(/<script[^>]*>.*<\/script>/gm, "");
return { cleanData: cleanData, js: js };
} else {
return { cleanData: data, js: js };
}
};
In this way I get the same result I had with jquery, even better, because on the DOM there is no longer any trace of the script tag
Now, my questions are:
Am I doing things right?
Is there a better way to do this?
Am I completely out of the way?
Your way is a correct way. For security reasons (XSS) the script tag is not run when using innerHTML.
https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML#security_considerations
Alternative: In MDN you can find hacky code like this to work around from the backend without touching the frontend. I would not recommend this way.
const name = "<img src='x' onerror='alert(1)'>";
el.innerHTML = name; // shows the alert

apply IF statement from a JS to only a particular html file

Screen width as a condition to redirect to other url during on load
-I'm trying to do this for only specific html pages, but can't seem to unless I put the redirect function (given in the link) as a script within the specific HTML page I want to invoke this.
Here is a breakdown of what I have/need
I have 3 files (desktop.html, mobile.html, script.js).
I want the desktop.html to redirect (or load) automatically the mobile.html if the screen width is < 992px.
The code I want to use will be kept in a separate JS file which is called script.js, this file already has a bunch of named functions that are within a variable. All the named functions (so far) are triggered when the user clicks on a button that corresponds. But I wont have a button that 'triggers' for redirecting to mobile.html page, as it should be automatic if the screen width is < 992px
Example of JS file
var name = {
first: function() {
<---! Does something when a button is pushed on my site --->
},
second: function() {
<---! Does something when a different button is pushed on my site --->
},
window.onload = redirectMobileHandler();
window.onresize = () => redirectMobileHandler();
function redirectMobileHandler() {
const width = Math.max(document.clientWidth || 0, window.innerWidth || 0);
if(width < 992) {
window.location = 'https://linktoyourmobilesite.com';
}
}
};
The HTML files reference the JavaScript files, so you have two options to only trigger that logic for one of your three HTML files:
Only link to the JavaScript file containing the logic in the target HTML file, by creating a new .js file that contains that logic (say index.js):
index.html
<link rel='index.js'>
index.js
var name = {
...
}
Reference the same JavaScript file in all three HTML files, but only call that function from within the target HTML file:
index.html
<button onclick='indexOnly()'>Button</button>
main.js
function indexOnly() {
var name = {
...
}
}
The latter option is preferable, as you can then make use of a template / header to handle loading all JavaScript files in a single location.
There is also technically a third option (though it is really just a combination of the two). If you use a framework like Angular, React or Vue, you'll get the concept of components, which automatically split this logic out for you, using encapsulation.
If I understand you correctly, you want to include the script to the target html file dynamically?
If so, there are numerous ways to accomplish this, for example:
var script = document.createElement('script');
script.src = 'script.js';
document.body.appendChild(script);
You can find more examples here.
However, I am not exactly sure what exactly you want to achieve. Please clarify, if my assumption is wrong.

Including dynamic script in jQuery [duplicate]

Any idea why the piece of code below does not add the script element to the DOM?
var code = "<script></script>";
$("#someElement").append(code);
The Good News is:
It's 100% working.
Just add something inside the script tag such as alert('voila!');. The right question you might want to ask perhaps, "Why didn't I see it in the DOM?".
Karl Swedberg has made a nice explanation to visitor's comment in jQuery API site. I don't want to repeat all his words, you can read directly there here (I found it hard to navigate through the comments there).
All of jQuery's insertion methods use
a domManip function internally to
clean/process elements before and
after they are inserted into the DOM.
One of the things the domManip
function does is pull out any script
elements about to be inserted and run
them through an "evalScript routine"
rather than inject them with the rest
of the DOM fragment. It inserts the
scripts separately, evaluates them,
and then removes them from the DOM.
I believe that one of the reasons jQuery
does this is to avoid "Permission
Denied" errors that can occur in
Internet Explorer when inserting
scripts under certain circumstances.
It also avoids repeatedly
inserting/evaluating the same script
(which could potentially cause
problems) if it is within a containing
element that you are inserting and
then moving around the DOM.
The next thing is, I'll summarize what's the bad news by using .append() function to add a script.
And The Bad News is..
You can't debug your code.
I'm not joking, even if you add debugger; keyword between the line you want to set as breakpoint, you'll be end up getting only the call stack of the object without seeing the breakpoint on the source code, (not to mention that this keyword only works in webkit browser, all other major browsers seems to omit this keyword).
If you fully understand what your code does, than this will be a minor drawback. But if you don't, you will end up adding a debugger; keyword all over the place just to find out what's wrong with your (or my) code. Anyway, there's an alternative, don't forget that javascript can natively manipulate HTML DOM.
Workaround.
Use javascript (not jQuery) to manipulate HTML DOM
If you don't want to lose debugging capability, than you can use javascript native HTML DOM manipulation. Consider this example:
var script = document.createElement("script");
script.type = "text/javascript";
script.src = "path/to/your/javascript.js"; // use this for linked script
script.text = "alert('voila!');" // use this for inline script
document.body.appendChild(script);
There it is, just like the old days isn't it. And don't forget to clean things up whether in the DOM or in the memory for all object that's referenced and not needed anymore to prevent memory leaks. You can consider this code to clean things up:
document.body.removechild(document.body.lastChild);
delete UnusedReferencedObjects; // replace UnusedReferencedObject with any object you created in the script you load.
The drawback from this workaround is that you may accidentally add a duplicate script, and that's bad. From here you can slightly mimic .append() function by adding an object verification before adding, and removing the script from the DOM right after it was added. Consider this example:
function AddScript(url, object){
if (object != null){
// add script
var script = document.createElement("script");
script.type = "text/javascript";
script.src = "path/to/your/javascript.js";
document.body.appendChild(script);
// remove from the dom
document.body.removeChild(document.body.lastChild);
return true;
} else {
return false;
};
};
function DeleteObject(UnusedReferencedObjects) {
delete UnusedReferencedObjects;
}
This way, you can add script with debugging capability while safe from script duplicity. This is just a prototype, you can expand for whatever you want it to be. I have been using this approach and quite satisfied with this. Sure enough I will never use jQuery .append() to add a script.
I've seen issues where some browsers don't respect some changes when you do them directly (by which I mean creating the HTML from text like you're trying with the script tag), but when you do them with built-in commands things go better. Try this:
var script = document.createElement( 'script' );
script.type = 'text/javascript';
script.src = url;
$("#someElement").append( script );
From: JSON for jQuery
It is possible to dynamically load a JavaScript file using the jQuery function getScript
$.getScript('http://www.whatever.com/shareprice/shareprice.js', function() {
Display.sharePrice();
});
Now the external script will be called, and if it cannot be loaded it will gracefully degrade.
What do you mean "not working"?
jQuery detects that you're trying to create a SCRIPT element and will automatically run the contents of the element within the global context. Are you telling me that this doesn't work for you? -
$('#someElement').append('<script>alert("WORKING");</script>');
Edit: If you're not seeing the SCRIPT element in the DOM (in Firebug for example) after you run the command that's because jQuery, like I said, will run the code and then will delete the SCRIPT element - I believe that SCRIPT elements are always appended to the body... but anyway - placement has absolutely no bearing on code execution in this situation.
This works:
$('body').append($("<script>alert('Hi!');<\/script>")[0]);
It seems like jQuery is doing something clever with scripts so you need to append the html element rather than jQuery object.
Try this may be helpful:
var fileref=document.createElement('script');
fileref.setAttribute("type","text/javascript");
fileref.setAttribute("src","scriptAnalytics.js");
document.getElementsByTagName("head")[0].appendChild(fileref);
I want to do the same thing but to append a script tag in other frame!
var url = 'library.js';
var script = window.parent.frames[1].document.createElement('script' );
script.type = 'text/javascript';
script.src = url;
$('head',window.parent.frames[1].document).append(script);
<script>
...
...jQuery("<script></script>")...
...
</script>
The </script> within the string literal terminates the entire script, to avoid that "</scr" + "ipt>" can be used instead.
Adding the sourceURL in the script file helped as mentioned in this page:
https://blog.getfirebug.com/2009/08/11/give-your-eval-a-name-with-sourceurl/
In the script file, add a statement with sourceURL like "//# sourceURL=foo.js"
Load the script using jQuery $.getScript() and the script will be available in "sources" tab in chrome dev tools
Your script is executing , you just can't use document.write from it. Use an alert to test it and avoid using document.write. The statements of your js file with document.write will not be executed and the rest of the function will be executed.
This is what I think is the best solution. Google Analytics is injected this way.
var (function(){
var p="https:" == document.location.protocol ? "https://" : "http://";
d=document,
g=d.createElement('script'),
s=d.getElementsByTagName('script')[0];
g.type='text/javascript';
g.src=p+'url-to-your-script.js';
s.parentNode.insertBefore(g,s); })();
You don't need jQuery to create a Script DOM Element. It can be done with vanilla ES6 like so:
const script = "console.log('Did it work?')"
new Promise((resolve, reject) => {
(function(i,s,o,g,r,a,m){
a=s.createElement(o),m=s.getElementsByTagName(o)[0];
a.innerText=g;
a.onload=r;m.parentNode.insertBefore(a,m)}
)(window,document,'script',script, resolve())
}).then(() => console.log('Sure did!'))
It doesn't need to be wrapped in a Promise, but doing so allows you to resolve the promise when the script loads, helping prevent race conditions for long-running scripts.
Append script to body:
$(document).ready(function() {
$("<script>", { src : "bootstrap.min.js", type : "text/javascript" }).appendTo("body");
});
Another way you can do it if you want to append code is using the document.createElement method but then using .innerHTML instead of .src.
var script = document.createElement( 'script' );
script.type = 'text/javascript';
script.innerHTML = 'alert("Hey there... you just appended this script to the body");';
$("body").append( script );
I tried this one and works fine. Just replace the < symbol with that \x3C.
// With Variable
var code = "\x3Cscript>SomeCode\x3C/script>";
$("#someElement").append(code);
or
//Without Variable
$("#someElement").append("\x3Cscript>SomeCode\x3C/script>");
You can test the code here.
Can try like this
var code = "<script></" + "script>";
$("#someElement").append(code);
The only reason you can't do "<script></script>" is because the string isn't allowed inside javascript because the DOM layer can't parse what's js and what's HTML.
I wrote an npm package that lets you take an HTML string, including script tags and append it to a container while executing the scripts
Example:
import appendHtml from 'appendhtml';
const html = '<p>Hello</p><script src="some_js_file.js"></script>';
const container = document.getElementById('some-div');
await appendHtml(html, container);
// appendHtml returns a Promise, some_js_file.js is now loaded and executed (note the await)
Find it here: https://www.npmjs.com/package/appendhtml
Just create an element by parsing it with jQuery.
<div id="someElement"></div>
<script>
var code = "<script>alert(123);<\/script>";
$("#someElement").append($(code));
</script>
Working example: https://plnkr.co/edit/V2FE28Q2eBrJoJ6PUEBz

How to include plain JavaScript in a React app?

I'm trying to create a React page which includes a p5 sketch, but doing so seems to require me to rewrite standard JavaScript I would normally run in a browser to make it work as a react component.
For example, I'd like to have React serve this code to the client:
function setup() {
createCanvas(600, 600);
}
function draw() {
background(0);
}
But I can't find a way to just have React give the client a JavaScript file. Instead I have to create a component like this:
export default function(p: p5) {
p.setup = function setup() {
p.createCanvas(600, 600);
};
p.draw = function draw() {
p.background(0);
};
}
This might seem trivial but if my team and I can include code that we've already written which works outside of react without having to rewrite everything would make things much easier.
One way to solve the problem is to just place the file in the public directory of React and just serve it statically along with index.html, but I'd prefer to only give the client the file when it needs it instead of just serving every file at once. If I could just have a component import the JavaScript file and send it like it can do with images, that would be exactly what I'm looking for.
Edit: Sorry, to clarify what I meant, Node is what's actually serving things, what I want is when React renders a page it will also run JavaScript code as if it were written in a <script> tag in the HTML page.
I've solved it. Essentially I put all of the code I want to run in a file like sketch.js but surround it in a function which is exported:
export default function Sketch() {
function setup() {
createCanvas(600, 600);
}
function draw() {
background(0);
}
}
Then in app.js you can do something like:
import Sketch from './sketch';
Sketch();
That will run all of the code in that function in the client's browser.
So just an option, we do this for optionally loading certain scripts on our app. In your component on the constructor (or maybe the willMount, play around with it) create a new script tag and append that script tag to the head of you app. This will cause the script to only be run when this component is rendered (Depending on where you called the function to add the script tag). You might also have to think about removing the script tag depending on what your doing, but you get the idea.
The function would look something like this:
addScriptTag = () => {
script = document.createElement('script');
script.src = [url/path of the javascript you want to server from your node server];
// Or just set the javascript on the script tag by adding innerText and type arts
document.head.appendChild(script)
}
then do something like:
constructor() {
this.addScriptTag();
}

How do you get the contextPath from JavaScript, the right way?

Using a Java-based back-end (i.e., servlets and JSP), if I need the contextPath from JavaScript, what is the recommended pattern for doing that, any why? I can think of a few possibilities. Am I missing any?
1. Burn a SCRIPT tag into the page that sets it in some JavaScript variable
<script>var ctx = "<%=request.getContextPath()%>"</script>
This is accurate, but requires script execution when loading the page.
2. Set the contextPath in some hidden DOM element
<span id="ctx" style="display:none;"><%=request.getContextPath()%></span>
This is accurate, and doesn't require any script execution when loading the page. But you do need a DOM query when need to access the contextPath. The result of the DOM query can be cached if you care that much about performance.
3. Try to figure it out within JavaScript by examining document.URL or the BASE tag
function() {
var base = document.getElementsByTagName('base')[0];
if (base && base.href && (base.href.length > 0)) {
base = base.href;
} else {
base = document.URL;
}
return base.substr(0,
base.indexOf("/", base.indexOf("/", base.indexOf("//") + 2) + 1));
};
This doesn't require any script execution when loading the page, and you can also cache the result if necessary. But this only works if you know your context path is a single directory -- as opposed to the root directory (/) or the multiple directories down (/mypath/iscomplicated/).
Which way I'm leaning
I'm favoring the hidden DOM element, because it doesn't require JavaScript code execution at the load of the page. Only when I need the contextPath, will I need to execute anything (in this case, run a DOM query).
Based on the discussion in the comments (particularly from BalusC), it's probably not worth doing anything more complicated than this:
<script>var ctx = "${pageContext.request.contextPath}"</script>
Got it :D
function getContextPath() {
return window.location.pathname.substring(0, window.location.pathname.indexOf("/",2));
}
alert(getContextPath());
Important note: Does only work for the "root" context path. Does not work with "subfolders", or if context path has a slash ("/") in it.
I think you can achieve what you are looking for by combining number 1 with calling a function like in number 3.
You don't want to execute scripts on page load and prefer to call a function later on? Fine, just create a function that returns the value you would have set in a variable:
function getContextPath() {
return "<%=request.getContextPath()%>";
}
It's a function so it wont be executed until you actually call it, but it returns the value directly, without a need to do DOM traversals or tinkering with URLs.
At this point I agree with #BalusC to use EL:
function getContextPath() {
return "${pageContext.request.contextPath}";
}
or depending on the version of JSP fallback to JSTL:
function getContextPath() {
return "<c:out value="${pageContext.request.contextPath}" />";
}
Reviewer the solution by this
Checking the solution of this page, make the following solution I hope it works:
Example:
Javascript:
var context = window.location.pathname.substring(0, window.location.pathname.indexOf("/",2));
var url =window.location.protocol+"//"+ window.location.host +context+"/bla/bla";
I render context path to attribute of link tag with id="contextPahtHolder" and then obtain it in JS code. For example:
<html>
<head>
<link id="contextPathHolder" data-contextPath="${pageContext.request.contextPath}"/>
<body>
<script src="main.js" type="text/javascript"></script>
</body>
</html>
main.js
var CONTEXT_PATH = $('#contextPathHolder').attr('data-contextPath');
$.get(CONTEXT_PATH + '/action_url', function() {});
If context path is empty (like in embedded servlet container istance), it will be empty string. Otherwise it contains contextPath string
A Spring Boot with Thymeleaf solution could look like:
Lets say my context-path is /app/
In Thymeleaf you can get it via:
<script th:inline="javascript">
/*<![CDATA[*/
let contextPath = /*[[#{/}]]*/
/*]]>*/
</script>

Categories