Import, fetch differences in http request and path - javascript

I am trying to understand how "import" works in client side javascript/typescript in terms of how data is exchanged with the server. Let us consider the following example:
Example code
page.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf8">
</head>
<body>
Something
<script type="module">
import * as Test from "./Test/test.js";
</script>
</body>
</html>
Test/test.js
import * as Test2 from "./test2.js"
export const test=0;
Test/test2.js
export const test2=0;
Question 1
What happens when page.html is loaded ? My understanding is that we probably have:
Executing the script of page.html
It requires to import "./Test/test.js". I assume a Get request is performed at "/Test/test.js".
Executing the script "./Test/test.js".
It requires to import "./test2.js". I assume a Get request is performed at "/Test/test2.js". I assume somehow the path of "/Test/test.js" was stored so that the relative path "./test2.js" is transformed into "/Test/test2.js".
Continuing
Is this correct ?
Question 2
Now let us imagine we have a file "Test/data.json" which is used in "Test/Test2.js". This file is "fetched" using the fetch command. My understanding is that the fetch command should take the path relative to the loaded page (i.e page.html) and thus should be "/Test/data.json". This behaves differently from how the import statement behaves (relative to current file).
Since I want my fetch path to be indifferent from the path of the loaded page, I have been "cheating" and have created a file "Test/data.js" instead of "Test/data.json" which contains exactly the data of "Test/data.json" but in an object. Is this a bad practice? If so, is there a good way to obtain what I wish?
Question 3
I just realized that I kind of have the same problem with css imports. It would be nice to import the css at first load of my component using a relative path (this way the component does not need to know where the loaded page is).
Possible (but looking for better) answer to Questions 2 and 3
Have a precompiler add a variable for each html and js file containing the path relative to the root project directory. Have a global variable containing the path of the loaded page. Compute at runtime the path of the file that I which to load. Is there a better way... ? Or am I completely doing things badly? Or am I just aiming for a design that cannot be achieved using basic javascript and I should just go all in on angular?

Related

Is <script src="file.js"> the same as <script src="./file.js">?

In an HTML file, are the following two script includes functionally equivalent?
index.html
<script src="file.js"></script>
and
<script src="./file.js"></script>
From what I remember, when files are resolved:
(1) The current directory of the HTML file is used by default for resolving relative paths,
(2) So "file.js" and "./file.js" are resolved using the path/directory of the HTML file.
When I experiment with this, they work the same in my test browser/server, but when it's behind an nginx load balancer with a route it is failing. But I think this is a different issue which would be an entirely different question.
EDIT: The value of the PATH environment variable and the rules for executable files lookup are unrelated to the way that HTML <script src="file.js"> is handled.
While the MDN docs for script are not helpful, this article http://brianvanderplaats.com/2017/01/16/understanding-relative-urls/
covers the issue and explains how relative paths are handled.
How it works
The way a script is handled is not determined by the server but is instead determined by the browser. The browser parses the element and makes an HTTP GET request to ask for the javascript file.
For example, if the following browser page were opened http://host.com/app/dir1/index.html and the following script element was found:
<script src="file.js"></script>
When examined in Chrome's devtools I see that an HTTP GET for the file being sent to the server.
http://host.com/app/dir1/file.js
What the standard says
The HTML standard in 4.3.1 The script element says:
If the element has a src attribute, then the value of that attribute must be resolved relative to the element, and if that is successful, the specified resource must then be fetched, from the origin of the element's Document.
This means relative to the index.html file and then requested via the document's origin which is document.location.origin. NOTE: Technically, the element refers to DOM element but I'm keeping it simple.
If we change the index.html file to include a dot, then we see the same HTML GET is issued. So this:
<script src="./file.js"></script>
Also results to the same HTTP GET being issued:
http://host.com/app/dir1/file.js
Hopefully this clearly that src="file.js" is functionally the same as src="./file.js".
Current is changed when <base> element is used
It is worth noting that if the <base> element exists, then it will be used instead of the current location. For example,
<html>
<head>
<base href="https://just-a-test/dir1/">
<script src="./file33.js"></script>
</head>
<body>... rest not shown
Then the browser will issue an HTTP GET for
https://just-a-test/dir1/file33.js
Another interesting case is when a relative path uses a sub-directory, or dot-dot syntax to get to a parent directory ../images/img1.png.
In both cases, the browser resolves the name and issues and HTTP GET Request for the resource it believes is the correct name. So
<html>
<head>
<base href="https://just-a-test/dir1/">
</head>
<body>
<img src="../images/img1.png"></script>
</body>
</html>
results in an HTTP GET request to the following file.
https://just-a-test.com/images/img1.png
<base> can include filename
Lastly, the <base> element can include a filename such as:
<html>
<head>
<base href="https://just-a-test/dir1/index.html">
</head>
<body>
<img src="img44.png"></script>
</body>
</html>
When this happens, the filename is dropped and only the remaining path is used so in this case an HTTP GET request is made for this file.
https://just-a-test.com/dir1/img44.png
And not the file https://just-a-test.com/dir1/index.html/img44.png.
I bring this case up because a common bug is to leave off the trailing slash and wonder why things are not working. For example,
<html>
<head>
<base href="https://just-a-test/dir1">
</head>
<body>
<img src="img44.png"></script>
</body>
</html>
Results in an HTTP GET Request to
https://just-a-test.com/img44.png
Which might make you think it wasn't working. This happens because dir1 is viewed just like index.html in the previous example and ignored for the purposes of issuing the HTTP GET requests.
Documentation on <base>
The MDN documentation for base is here and the HTML5 standard for <base> is here. My hightlights are:
If the document has no elements, then the browser uses location.href. A base can include a filename.
If multiple elements are used, only the first href and first target are obeyed — all others are ignored.
A base element must have either an href attribute, a target attribute, or both.
The base element has to be put in the <head> element.
The base element does not have a closing tag.
There is no difference between the two from web browser perspective.
However, on *nix systems, for example in shell, file.js would be searched in $PATH, while ./file.js will be searched in current directory.

How can I serve remote React app with a script tag?

We have at our company a react app (built with create-react-app) which is served today via an iframe, which works well.
A need has risen to serve the app (which is always embedded within other pages), with a script tag alone (no iframe tag).
I thought of something like:
<div id="app-placeholder"></div>
<script src="https://our-app.com/init.js"></script> // this will create a function called window.InitMyApp
<script>
InitMyApp('#app-placeholder', 'token', otherOptions)
</script>
I've tried to create init.js file in the react app's public folder. I can access the file.
From that file, how can I render the react app itself to the given selector (#app-placeholder)?
Because this file is served as-is, and doesn't get transpiled by webpack/babel, I cannot use import/jsx/require() and other stuff.
Am i on the right track?
Should I manually transpile this file?
Are there any other solutions to this rendering method?
You should try configuring the compiler with { output.library }. This should produce a compilation output that's ready for distribution, which means you can easily reference it in another document (and not need to worry about, say, transpiling/optimizing sources, because this was already performed by webpack).
Here's an example of a multi-part library produced by webpack. As you can see, the entrypoint exports are being assigned to window.

Using external JavaScript files in a WinForms/WPF WebBrowser control

I found a cool feature on a website, implemented in JavaScript, I'd like to use it as is in my desktop application (for personal use).
During my experiments I managed to generate custom HTML on the fly, feed it to the browser using webBrowser1.DocumentText = [my generated HTML]
I've managed to put some inline JavaScript into the HTML, and hook it up via a ScriptManager so that I can call the JavaScript from my C# code, pass a value to it, and get a return value.
But the feature I'm trying to use is a bit more complicated: it's no less than 10 JavaScript files. 2 of them are referenced directly in the web page the usual way <script src="/js/script1.js" type="text/javascript"></script>
The other 8 are loaded in one of the scripts:
var elem = document.createElement("script");
elem.type = "text/javascript";
elem.src = "/js/" + filename;
document.body.appendChild(elem);
These 8 files are in fact data files, even though the data is represented in JavaScript. They're pretty large, over 1MB each. Stuffing it all into the HTML file seems quite stupid. Also, the script that loads the data creates a "file map" and further refers to the data based on which file it's in:
var fileMap = [
[/[\u0020-\u00ff]/, 'file1.js'],
[/[\u3000-\u30ff]/, 'file2.js'],
[/[\u4e00-\u5dff]/, 'file3.js'],
...
I don't want to resort to modifying the JavaScript, because it's not exactly my strong point. So the browser needs to "see" the js files in order to be able to use them. I thought of creating the file structure locally, and navigating the browser there. But I don't want any loose files in my solution. I'd like to have everything embedded if possible. And I doubt I can get the browser to navigate to an embedded resource, and see other embedded resources as files. Any idea how I could get around this?
EDIT:
I've tried to do it with local files. No luck. I get the HTML to load properly, but when I try to invoke a JavaScript call, nothing happens. I tried pointing the browser to those js files, to make sure they're there. They are. I tried an element with src attribute pointing to an image in the same subfolder as the script files. It gets rendered. It's as if external js files refuse to load.
I had a similar need as your scenario and I addressed it using two key points embedded in two other Stack Overflow answers. As noted by SLaks' answer here the first key is using the syntax file:/// as the prefix for an absolute path to external files. The second is using .Replace("\\", "/") for an absolute file path as listed in Adam Plocher's answer and one of his follow-up comments here.
In short, the final output for each external file in an HTML page will look something like:
<link href="file:///c:/users/david/myApp/styles/site.css" rel="stylesheet" type="text/css">
or
<script src="file:///c:/users/david/myApp/scripts/JavaScript1.js"></script>
Using the format in the samples above in my HTML file resulted in the WebBrowser control loading external CSS, image or script files.
The details and solving the scenario in the question
In the womd's answer in the first referenced SO answer above he used the method System.IO.File.ReadAllText() to load script files and embedded the text of the script files into the <head> tag. As you indicated in your question loading script files directly into the HTML page is not what you're looking to do.
The solution below involves using the same System.IO.File.ReadAllText() method but loads the text of the HTML page instead. The premise works similar to the Razor View Engine in ASP.NET.
The main idea in the solution below involves adding a temporary string in an HTML page that will be loaded into the WebBrowser control and then replacing this temporary string in a C# method in my app just before the HTML page is set to be loaded into the WebBrowser control.
Here are the basic steps to my solution:
Add a temporary string for each external reference in the HTML file.
Declare a variable for the absolute path in a script tag within the HTML file. This step is not necessary unless you're going to use the absolute path elsewhere within your JavaScript code. Your scenario involves delay loading external script files via JavaScript code so this step was necessary.
Modify the src property in the JavaScript code that delay loads the other script files with the absolute path variable.
Add a method in your app to loads the HTML page file as a text string and then replaces all temporary string instances with an absolute path containing the prefix 'file:///'. The absolute path should have forward slashes.
Set the 'DocumentText' property on the WebBrowser control to the updated HTML.
Set the 'Copy to Output Directory' of each external file in your project to 'Copy always' or 'Copy if newer'. This step may not be necessary if you have a fixed location to your external files and that location is not within the build or publish directory used by Visual Studio.
The following are the details for each step. I added a lot of detail that you can skip. I was verbose to reduce any confusion since the steps make changes to several places in the project.
1. Using a temporary string
I used the string "/ReplaceWithAbsolutePath/" but you can use any distinct text. Each reference to an external file in the HTML page looks like:
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title></title>
<link href="/ReplaceWithAbsolutePath/styles/site.css" rel="stylesheet" type="text/css">
<script type="text/javascript">
var absolutePath = "/ReplaceWithAbsolutePath/";
</script>
</head>
<body>
<p>My web page</p>
<script src="/ReplaceWithAbsolutePath/scripts/JavaScript1.js"></script>
</body>
</html>
2. Declare absolute path variable
Note in the above HTML page I listed a <script> tag with the declared variable 'absolutePath' set to the temporary string. (In the HTML page above the variable is added a global variable and that is not necessarily best practice. You can declare the variable within a namespace instead of declaring it in the global namespace.)
3. Modify the delay load script to include absolute path variable
Add the 'absolutePath' variable to your JavaScript file that delay loads other JavaScript files containing your data.
elem.src = absolutePath + "/js/" + filename;
4. C# method to replace all temporary string instances
Within your project add the following line to your form load event handler or place this line somewhere in your initialization of the WebBrowser control.
webBrowser1.DocumentText = GetUpdatedHtmlWithAbsolutePaths("/ReplaceWithAbsolutePath/", "HTMLPage1.html");
Add the following method to your code. Update the call to the method in the line above with the name of the class instance where the following method is placed.
// The result of this method will look like the following example:
// <script src="file:///c:/users/david/documents/myApp/scripts/JavaScript1.js"></script>
public string GetUpdatedHtmlWithAbsolutePaths(string tempPathString, string htmlFilename)
{
// Get the directory as the application
// stackoverflow.com/questions/674857/should-i-use-appdomain-currentdomain-basedirectory-or-system-environment-current
// Note that the 'BaseDirectory' property will return a string with trailing backslashes ('\\')
string appDirectory = AppDomain.CurrentDomain.BaseDirectory;
// Replace '//' with '/' in the appDirectory string
appDirectory = appDirectory.Replace("\\", "/");
// Read all of the HTML text from the HTML page file
string html = System.IO.File.ReadAllText(appDirectory + #"\" + htmlFilename);
// Replace all '/ReplaceWithAbsolutePath/' strings within the HTML text with
// the absolute path on the local machine
html = html.Replace(tempPathString, "file:///" + appDirectory);
return html;
}
5. Set the DocumentText property of the WebBrowser control
I added the initialization of the WebBrowser control in the form load event handler but you can, of course, add the line that sets the DocumentText property wherever you initialize your WebBrowser control.
private void Form1_Load(object sender, EventArgs e)
{
// Set the document text of the web browser control with the updated HTML
webBrowser1.DocumentText = GetUpdatedHtmlWithAbsolutePaths("HTMLPage1.html");
}
6. Set the 'Copy to Output Directory' of each external file
Take a look at the answer posted by Matthew Watson in this Stack Overflow question if you want your external files included in your solution/project file structure.
You can add files to your project and select their properties: "Build
Action" as "Content" and "Copy to output directory" as "Copy Always"
or Copy if Newer (the latter is preferable because otherwise the
project rebuilds fully every time you build it).
Then those files will be copied to your output folder.
This is better than using a post build step because Visual Studio will
know that the files are part of the project. (That affects things like
ClickOnce applications which need to know what files to add to the
clickonce data.)
In short, add the external file to your project. You can add the external to any subfolder in your project. (In Visual Studio 2013 or 2015 -- I don't have VS2012) Right-click on the external file in the Solution Explorer and select Properties from the context menu. The Properties pane will be displayed. In the Properties pane change the setting for 'Copy to Output Directory' to 'Copy always' or 'Copy if newer'.
Use View Source to verify absolute path strings
Run your project and it should load your external files in the WebBrowser control. Assuming you have not set the property wbChartContainer.IsWebBrowserContextMenuEnabled = false; in code or in the Properties pane for WebBrowser control you can right-click on the WebBrowser control when your form is running. Click 'View Source' from the context menu and check the paths to your external resources in the View Source window.

javascript file not loading

Trying to import my js file from my page.
My page is in webcontent/mydomain/templates/page.xhtml
My js is in webcontent/mydomain/test/scripts
In page.xhtml
<script type="text/javascript" src="../test/scripts/test.js"></script>
But still the script is not getting picked.
Can anyone tell how I need to give the path in src.
Try this:
<script src="/test/scripts/test.js"></script>
Provided that webcontent is the root of public web content and thus /mydomain is also a public folder and thus your JavaScript is standalone available by http://localhost:8080/context/mydomain/test/scripts/test.js, assuming a domain of http://localhost:8080 and a context path of /context, then the following should do:
<script src="#{request.contextPath}/mydomain/test/scripts/test.js"></script>
This will generate a domain-relative URL with a dynamically inlined context path, which is much more robust than fiddling with ../ which would make the URI relative to the current request URI (as you see in browser's address bar) and not to the physical location of the template file as many starters incorrectly assume.

javascript, get script path from inside the script

Is it possible to get the Javascript server path?
For example, I have a javascript file that is in a directory, and in that directory there are some php scripts that I want to "post" to.
When I include the javascript file in my home page
<script type="text/javascript" src="assets/js/some.js"></script>
I can't access say a file like /post.php that resides in the assets/js/ directory using relative paths since the javascript is not being "run" in the directory that the home page is in.
Obviously, I can specify the path, but I was trying to think of a more robust way.
I know you can get the "script" tags and get the url of the file, and I could probably work from there, but I didn't know if such a function exists, that would just tell you.
Thank you!
Create a script that is served from bla.php and place it before your some.js
<script type="text/javascript" src="bla.php"></script>
Inside bla.php, render a javascript class and store your data in it.
i.e.
function MyConstants() {
this.HELLO = "hello";
this.POST_PATH = "/etc.php";
}
You can access the constants (or anything else) via:
var consts = new MyConstants();
alert(consts.HELLO);
Hope this helps! :-)
You can try to use a cross-browser stack tracer to get the current file name
however there are many cases where your idea this might break, For example if you add a JS cacher that complies all necessary JS files into one and compresses them.
I would say that a smarter approach is to have a principle in place with which you can figure out the urls for your php skripts, like RubyOnRails has routes

Categories

Resources