I'm trying enter code here in to execute an imported module from a HTML page. I get to see that it's partially getting executed. I need help. My code files are test.html, main.js and say.js. These files are produced below in the same order.
test.html:
<html>
<head>
<script type="module" src="./main.js"></script>
</head>
<body onload="sayHi('Manish')">
</body>
</html>
main.js:
import { sayHi } from './say.js';
sayHi('MK');
say.js:
export function sayHi(user) {
alert('Hello, { $user }');}
This code partially executes
Then after this partial execution, it gives an error
Uncaught ReferenceError: sayHi is not defined
at onload (test.html:7)
The picture of the error is as shown below:
This is the error that says sayHi function is not recognized. Why?
What am I doing wrong here?
One of the great things about modules is that top level declarations, etc., in them don't create globals. One of the bad things about onxyz-attribute-style event handlers is that the functions you call with them have to be globals. Your sayHi isn't a global, so onload="sayHi('Manish')" fails because it doesn't have access to sayHi.
Which is a good thing.
Instead, just call the function from main.js:
import { sayHi } from './say.js';
sayHi('MK');
sayHi('Manish');
Because module scripts are automatically deferred until the end of HTML processing, you know that won't happen until all the HTML is loaded. This is covered by a great graphic in this section of the spec, duplicated here:
If you want to wait longer, until the load event (which doesn't fire until all images and such are loaded), use a modern event handler to do that:
import { sayHi } from './say.js';
sayHi('MK');
window.addEventListener("load", () => {
sayHi('Manish');
});
If you need information from the element you hooked the event on, use a traditional function and access the element as this, or accept the event parameter and use event.currentTarget (you can also use event.target for the element that the event targets, which could be within the element you hooked the event on). So for instance, suppose you have:
<button type="button" data-name="Manish" id="btn-say-hi">
you could have:
import { sayHi } from './say.js';
document.getElementById("btn-say-hi").addEventListener("click", function() {
sayHi(this.getAttribute("data-name"));
});
Also note that as Vikas Saini pointed out your say.js is using a string literal instead of a template literal (and although he/she didn't mention it, also has the wrong syntax for a substitution), so you'll actually see the text Hello { $user } instead of seeing Hello MK or Hello Manish. Either use a template literal with the correct form of substitution (${user}, not { $user }):
export function sayHi(user) {
alert(`Hello, ${user}`);
}
or simple string concatenation:
export function sayHi(user) {
alert("Hello, " + user);
}
Classic case of String interpolation
Use
export function sayHi(user) { alert(`Hello, ${user}`);}
Notice ` in place of ' or "
Reference https://campushippo.com/lessons/how-to-do-string-interpolation-with-javascript-7854ef9d
For the Error, bind the window to load
window.addEventListener("load", () => {
sayHi('testing');
});
Basically my objective was to pass some parameter from the HTML script to the main.js file. I have got the required after both of you have given me hints, especially Vikas Saini. The suggestion of adding an event listener in the main.js file helped a lot. Thanks much. I'm posting the corrected and the latest code files for the benefit of ES6 beginners like me.
The test.html file contents
<html>
<head>
<script type="module" src="./main.js"></script>
</head>
<body>
<input type="text" id="btn-say-hi"label="fill here" placeholder="Fill in here and click"/>
</body>
</html>
The main.js file contents
import { sayHi } from './say.js';
document.getElementById("btn-say-hi").addEventListener("click", function() {
sayHi(this.value);
});
The say.js file contents
export function sayHi(user) { alert(`Hello, ${user}`); }
Kindly note: All these files are in the same directory/folder. I could change the text item value and could get the required execution based on my inputs.
Thanks much to T.J. Crowder and a special thanks to Vikas Saini for the code snippet pertaining to adding the event listener. That was a golden suggestion.
Thanks much guys.
Regards
Manish.
Related
I'm looking for a way to pass functions as parameters to the script tag. For example, to make the following work:
<script src="http://path/to/widget.js?param_a=1¶m_b=3" data-myfunc={myfunction()}></script>
<script>
myfunction() {
console.log("hello world")
}
</script>
And then trigger the function from the script.
Since we can pass values in attributes and capture using getAttributes : ref
Try this
<script>
// move function definition above and pass function ref - don't call that function
myfunction(){
console.log("hello world")
}
</script>
<script src="http://path/to/widget.js?param_a=1¶m_b=3" data-myfunc={myfunction}></script>
Yes there is a way!
you can delete the " () "
just turn :
<script src="http://path/to/widget.js?param_a=1¶m_b=3" data-myfunc={myfunction()}></script>
into:
<script src="http://path/to/widget.js?param_a=1¶m_b=3" data-myfunc={myfunction}></script>
And over!
It's my pleasure to help you!
by the way if you are interested, please help me also:
The is my question
that's pretty easy however it won't be accurate as you don't know which script tag will work first or if it will be compiled using inline or not.
if it uses inline your code will not work and you have to use the server to render javascript instead
here is an example using pure javascript. in my understanding you want after loading script /widget.js it will execute function stored in data-myfunc:
widget.js
if (document.currentScript) {
const script = document.currentScript.getAttribute('data-myfunc')
if (script) {
new Function(`
// sandbox faker
const window = undefined;
const document = undefined;
const Document = undefined;
const Window = undefined;
// run script
${script}
`)()
}
} else {
console.warn('widget.js loaded inline. Not working')
}
note if you want to declare the function myFunc after the script /widget.js you have to edit my code to use events like DOMContentLoaded to make sure the function exists
I have as basic onClick in a button that outputs an alert
function test(){
alert('here');
}
<button onClick="test()">Press</button>
In StackBlitz this doesn't work - https://stackblitz.com/edit/js-umarwe?embed=1&file=index.js&hideNavigation=1
Is this because of ES6
How does this work in ES6
Why it doesn't work
The reasons it is not working is that behind Stackblitz there is an asset building system which treats your Javascript code as modules.
That means that variables defined in those modules are only available inside those modules and do not become attached to the global namespace (as you expected and seem to be used to).
Minimum required to fix it
To achieve that, you need to explicitly attach those variables to the global object, which inside a browser happens to be window.
Simply adding the following line at the end of your index.js file makes your code work:
window.test = test;
A better way
Please note that using inline event handlers directly on the element (like onclick) is considered bad practice (and does have practical disadvantages, but that would lead too far). Instead, you should use Javascripts Element.prototype.addEventlistener() function. Steps to get there:
Add an id to your button so your Javascript can find it:
<button id="testButton">Press</button>
Next, put that element in a variable:
const button = document.getElementById('testButton');
Last step: Add the event listener for the click event:
button.addEventListener('click', test)
Here's the full index.js for that refactored version:
// Import stylesheets
import './style.css';
function test(){
alert('here');
}
const button = document.getElementById('testButton');
button.addEventListener('click', test);
How to make it even safer and better
One more note: If you place the script tag loading the Javascript in the head section of the document, you either need to add a defer attribute on the tag, or wrap the part of the code that needs to access the DOM in a DOMContentLoaded event handler:
// Import stylesheets
import './style.css';
function test(){
alert('here');
}
document.addEventListener('DOMContentLoaded', function () {
const button = document.getElementById('testButton');
button.addEventListener('click', test);
}
Otherwise the HTML has not yet been parsed by the browser when your Javascript tries to find the button and attach the event listener.
Modify test function declaration in index.js file:
window.test = function(){
alert('here');
}
It doesn't recognize test function on click event because test function you declared is not in the same scope.
You can do this.
window.test = () =>{
alert('here');
}
I don't know stackBlitz..
but, I understand loading index.js
so window.test = () => {} ...
I've just read about the use of HTML imports for component encapsulation.
<link rel=import href="import.html">
The file import.html would include everything that's needed for the component.
One big problem, though: Javascript functions and variables inside import.html become part of the window namespace, which means there's no encapsulation whatsoever.
Two different components which happen to have a function with the same name will collide and one of the functions will be overridden.
Do HTML imports provide any form of javascript encapsulation that didn't exist before?
Example:
main.html
<link rel=import href="import1.html">
<link rel=import href="import2.html">
<script>
console.log( moduleFunction() ) ; //`moduleFunction` can be called as if it was defined in the outter document
</script>
import1.html
<script>
function moduleFunction(){
return 'module1' ;
}
</script>
import2.html
<script>
function moduleFunction(){
return 'module2' ;
}
</script>
No. However, wrapping everything in a function or having a global object that you store all your variables in inside import.html would work.
It looks like you already had the answer.
Javascript functions and variables inside import.html become part of the window namespace, which means there's no encapsulation whatsoever.
Two different components which happen to have a function with the same name will collide and one of the functions will be overridden.
Do HTML imports provide any form of javascript encapsulation that didn't exist before?
Not much from the spec.
HTML5 Rocks:
Script in the import is executed in the context of the window that contains the importing document. So window.document refers to the main page document.
This is not widely supported.
Do HTML imports provide any form of javascript encapsulation that
didn't exist before?
No, not the use of rel="import" alone. It is up to the developer to incorporate the appropriate logic into the procedure if two separate HTML documents could potentially include <script> elements where functions are declared that have the same identifiers, respectively; and to decide which identifier will be used at main window.
<link> You can use let, try..catch. Since <link> elements are blocking, see link elements block DOM parsing too, we use the order of the <link> in HTML to arrange the check for the declaration of the identifier. That is, moduleFunction should always be defined from "import1.html", unless the requirement is to implement a different order of checking for specific variables being declared or not
<script>
let moduleFunction;
try {
moduleFunction = function moduleFunction() {
return 'module1';
}
} catch (err) {
console.error("module 1 error", err)
}
</script>
<script>
moduleFunction = moduleFunction || void 0;
if (!moduleFunction) {
try {
moduleFunction = function moduleFunction() {
return 'module2';
}
} catch (err) {
console.error("module2 error", err)
}
}
</script>
plnkr http://plnkr.co/edit/lzHkc8pydD7q17ldgnrq?p=preview
I'm just starting with Flask, so I may be overlooking something very obvious. I have loaded my javscript file with this:
<script src="{{ url_for('static', filename='Page.js') }}"></script>
then I try to instantiate an object from that js file:
<script>
var page = new Page("index");
</script>
In Page.js, I have this:
var Page = function(page) {
alert("init");
<some other things>
}
<and then some object methods Page.prototype.init_standard = function() {} etc>
The alert isn't alerting, though I am expecting it to. Also, if I put an alert before the instantiation in the HTML page, I get the alert, if I put an alert on the line after the instantiation on the HTML page, I don't get the alert. I'm not sure if this is a Flask issue or javascript -- I'm quite new at both.
EDIT: To nip this possibility in the bud, the javascript file is getting loaded according to the development server, status 304
Page.js needs to just define a function that creates the object you want. So Page() should just be a function that ends with 'return this;'. Correction returning this is not strictly required for object creation.
function Page(page) {
alert("init");
... Your code and methods...
// Example Method:
this.foo = function(param) {
... function body ...
};
return this;
}
And then you can create an object with:
var page_object = new Page("index");
page_object.foo(some_data);
I figured it out after looking at the browser debugger. It said Page was not defined. After some trial and error, I realized I can't use {{ flask templating notation inside the javascript file, probably because it isn't loaded as a template so no parsing of it in that way occurs. Anyway, I ended up just moving the {{ }} link to the html template and passing it in as an argument to the javascript.
I have a javascript file that I'm am calling in the head of an HTML file, it's called custom.js, it defines this function:
var example = function(element){
console.log(element);
};
Then in the actual HTML document, just before the body tag closes, I try to initialize it by calling the same function like this
example('.header-background');
I get the error example is not a function, what exactly am I doing wrong? Thank you so much for your help.
here is a bit more context
<!DOCTYPE html>
<html>
<head></head>
<body>
// the main body of html
<script src="custom/custom.js"></script> // here is the file with the example function
example ('.header-background'); // here im calling the function
</body>
</html>
Change var example to window.example. If you declare a variable with var inside of a function or closure it isn't globally accessible.
e.g.,
function foo()
{
var test = 'hi';
}
console.log(test); // error, test isn't accessible
function foo()
{
window.test = 'hi';
}
console.log(test); // works
Global variables should generally be avoided there. There's most likely a better way to do what you want to do.
Make sure to the file where your function in the html page you are calling the function:
<script type="text/JavaScript" src="youtpathtothefile/custom.js"></script>
Hope it helps
Please make sure two things:
your javascript file is loaded after jQuery library.
your javascript is loaded after the every dom in the html file has finish loading.
Maybe that could help you.