document.getElementById not working with Webpack - javascript

I've been trying to implement basic Javascript with Webpack but have found a lot of trouble trying to get basic methods to work. I know it is probably a problem with my js loading before the DOM but all my fixes to this issue have been futile.
For instance, I'm trying to just run let taskinput = document.getElementById('new-task'); and when I search the taskinput variable in the console I get a return of Uncaught ReferenceError: taskinput is not defined(…).
I've tried quite a few different solutions to make this code operate after the DOM has loaded but nothing seems to work.
I tried the basic attempt of just putting my javascript file at the bottom of the DOM.
I've tried a basic js method like so:
document.onreadystatechange = function() {
if (document.readyState === "complete") {
initApplication();
}
function initApplication() {(... placed code in here ...)}
I've tried using jqueries
$( document ).ready(function() { });
I've tried injecting my javascript file into my HTML with the html-webpack-plugin
Is there something I'm missing with Webpack?
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Sample Site</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
</head>
<body>
<button id="new-task">click</button>
<script src="/assets/app.bundle.js"></script>
</body>
</html>
app.js
'use strict';
document.onreadystatechange = function() {
if (document.readyState === "complete") {
initApplication();
}
}
function initApplication() {
onButtonClick();
}
let onButtonClick = function() {
let taskInput = document.getElementById('new-task');
taskInput.onclick = alert('hey');
}

For instance, I'm trying to just run let taskinput = document.getElementById('new-task'); and when I search the taskinput variable in the console I get a return of Uncaught ReferenceError: taskinput is not defined(…).
First of all, module code in webpack bundles is run in its own scope, effectively in a closure, and not in the global scope (which you can access with window). Secondly, even if you go to your console and declare a variable with let, for example let a = 1, even though your console operates in the global (window) scope, if you try to do window.a you will get undefined because let variables do not get attached to the window scope. See this
Is there something I'm missing with Webpack?
Probably the fact that the code in your bundles, generated by webpack, does not run in the global scope, as explained above.
If you want to expose a variable to the global scope in order to be able to use it, for debugging purposes, in the console, declare it as window.variableName. Or you can add a breakpoint in your code, by adding debugger, after the variable you want to check out, without exposing it to the global scope.

Using the browser console you can only access variables which are declared globally, and if you define a JavaScript variable inside a function, it doesn't become a global variable. Also, variables declared using let never become global variables.
If you want to declare a variable globally, you can make it a property of window, for example:
window.taskinput = document.getElementById('new-task');
This might be useful for debugging, but avoid doing it in production code, as using global variables is considered a bad practice.

Related

OnChange function not defined when I reference my script as a module? [duplicate]

I have my HTML setup like this:
<script type="module" src="main.js"></script>
and all the ES6 modules work fine. The only problem is I now can't refer to anything from within DevTools (like using the Console and typing in a variable to see it's value or using a function manually).
How do I import modules whilst being able to use the DevTools? Thanks!
One way to make a variable accessable within DevTools is to create it on the window object:
// Variable in your module
var importantNumber = 1;
window.importantNumber = importantNumber;
This method works fine if you just have a couple of variables, but if you need to have access to a lot more variables within DevTools, I would recommend you go to the sources-tab in DevTools, search for your module and adding a breakpoint. When the execution pauses, you have access to all the variables within that module on the DevTools console.
If you want to be able to refer to variables created within the module from the console's global scope, you'll have to deliberately expose each such variable that you want to be visible from the console. Either assign each variable to window (probably not a good idea - the whole point of modules is to make things more modular, without global pollution), or perhaps assign a single object to window, to which you assign module variables. For example:
// in the console:
setTimeout(() => {
window.myModule.foo();
console.log(window.myModule.bar);
});
<script type="module">
function foo() {
console.log('doing foo');
}
const bar = 'a string bar';
const thisModule = { foo, bar };
window.myModule = thisModule;
// If you ever reassign variables, you'll have to reassign them on thisModule too
// or, only reference and reassign properties of thisModule, rather than create independent variables
</script>
For anyone else interested, if you're comfortable with it, use a bundler like Webpack. I don't believe (at least at this point) that the browser will by itself be able to use the DevTools on modules (the other solutions are quite janky, and aren't fantastic to work with).
Hopefully in the future, when all major browsers will be able to support ES6 modules without a bundler, we'll be able to use DevTools.
Using a Helper
I personally use a little helper function in development that allows me to expose a bunch a variables in a single expression. For example, it makes the following two blocks equivalent:
window.playerOne = playerOne;
window.someClass = someClass;
window.localData = localData;
globalize({playerOne, someClass, localData});
The helper looks like this:
const globalize = function(variables) {
Object.entries(variables).forEach(([name, value]) => window[name] = value);
};

trying to use variables in javascript outer the function scope

i m using node-red and i m injecting some json variables, i can only see them in function scope, how can i use them outer the function.
i've tried angularjs controller but it didn't work, maybe i did not used it correctly
<!DOCTYPE html>
<html>
<body>
<div>{{msg.payload}}</div>
<div>{{value}}</div><br>
<div>{{value1}}</div><br>
<div>{{value2}}</div><br> // i can see the contents
<md-button ng-click="send({payload:action()})">
</md-button>
<script>
(function(scope) {
scope.$watch('msg', function(msg) {
if (msg.payload.image) {
scope.value2 = msg.payload.image;
}
if (msg.payload.text) {
scope.value1 = msg.payload.text;
}
if (msg.payload.volume) {
scope.value = msg.payload.volume;
}
scope.action = function() {
return [scope.value, scope.value1, scope.value2];
}
});
})(scope);
</script>
</body>
</html>
Variables are only valid and accessible in the scope where they were created (and in "child-scopes").
If you want to have some global variable, you can just create a "global variable" (just google).
But since you are using Angular (though the old "angularjs" and not the new "Angular" version), there are better ways. In Angular (the new one, don't know about the old one) you can create and use something called "service". Those are some sort of global accessible object that can hold many variables and stuff to be used elsewhere in the code.
BUT: Depending on your use-case here, you may also create the variables on the outer angular-component and insert/pass them into the child-component. This way both can use and access it. Read more about binding/2-way-binding for that purpose.

How to make a variable global from inside a function [duplicate]

I need a few global variables that I need in all .js files.
For example, consider the following 4 files:
global.js
js1.js
js2.js
js3.js
Is there a way that I can declare 3 global variables in global.js and access them in any of the other 3 .js files considering I load all the above 4 files into a HTML document?
Can someone please tell me if this is possible or is there a work around to achieve this?
Just define your variables in global.js outside a function scope:
// global.js
var global1 = "I'm a global!";
var global2 = "So am I!";
// other js-file
function testGlobal () {
alert(global1);
}
To make sure that this works you have to include/link to global.js before you try to access any variables defined in that file:
<html>
<head>
<!-- Include global.js first -->
<script src="/YOUR_PATH/global.js" type="text/javascript"></script>
<!-- Now we can reference variables, objects, functions etc.
defined in global.js -->
<script src="/YOUR_PATH/otherJsFile.js" type="text/javascript"></script>
</head>
[...]
</html>
You could, of course, link in the script tags just before the closing <body>-tag if you do not want the load of js-files to interrupt the initial page load.
The recommended approach is:
window.greeting = "Hello World!"
You can then access it within any function:
function foo() {
alert(greeting); // Hello World!
alert(window["greeting"]); // Hello World!
alert(window.greeting); // Hello World! (recommended)
}
This approach is preferred for two reasons.
The intent is explicit. The use of the var keyword can easily lead to declaring global vars that were intended to be local or vice versa. This sort of variable scoping is a point of confusion for a lot of Javascript developers. So as a general rule, I make sure all variable declarations are preceded with the keyword var or the prefix window.
You standardize this syntax for reading the variables this way as well which means that a locally scoped var doesn't clobber the global var or vice versa. For example what happens here is ambiguous:
greeting = "Aloha";
function foo() {
greeting = "Hello"; // overrides global!
}
function bar(greeting) {
alert(greeting);
}
foo();
bar("Howdy"); // does it alert "Hello" or "Howdy" ?
However, this is much cleaner and less error prone (you don't really need to remember all the variable scoping rules):
function foo() {
window.greeting = "Hello";
}
function bar(greeting) {
alert(greeting);
}
foo();
bar("Howdy"); // alerts "Howdy"
Have you tried it?
If you do:
var HI = 'Hello World';
In global.js. And then do:
alert(HI);
In js1.js it will alert it fine. You just have to include global.js prior to the rest in the HTML document.
The only catch is that you have to declare it in the window's scope (not inside any functions).
You could just nix the var part and create them that way, but it's not good practice.
As mentioned above, there are issues with using the top-most scope in your script file. Here is another issue: The script file might be run from a context that is not the global context in some run-time environment.
It has been proposed to assign the global to window directly. But that is also run-time dependent and does not work in Node etc. It goes to show that portable global variable management needs some careful consideration and extra effort. Maybe they will fix it in future ECMS versions!
For now, I would recommend something like this to support proper global management for all run-time environments:
/**
* Exports the given object into the global context.
*/
var exportGlobal = function(name, object) {
if (typeof(global) !== "undefined") {
// Node.js
global[name] = object;
}
else if (typeof(window) !== "undefined") {
// JS with GUI (usually browser)
window[name] = object;
}
else {
throw new Error("Unkown run-time environment. Currently only browsers and Node.js are supported.");
}
};
// export exportGlobal itself
exportGlobal("exportGlobal", exportGlobal);
// create a new global namespace
exportGlobal("someothernamespace", {});
It's a bit more typing, but it makes your global variable management future-proof.
Disclaimer: Part of this idea came to me when looking at previous versions of stacktrace.js.
I reckon, one can also use Webpack or other tools to get more reliable and less hackish detection of the run-time environment.
Yes you can access them. You should declare them in 'public space' (outside any functions) as:
var globalvar1 = 'value';
You can access them later on, also in other files.

Prevent the pollution of the global scope

We're trying to enforce JavaScript best practices for the project we're working on, one of the limitations we're trying to set is to not pollute the global scope.
We have an HTML template used for every page structured like this:
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
</head>
<body>
<h1>Test</h1>
<!-- Page content here -->
<script src='https://code.jquery.com/jquery-2.1.0.js'></script>
<script src='https://ajax.googleapis.com/ajax/libs/angularjs/1.2.14/angular.js'></script>
<!-- Page scripts here -->
</body>
</html>
What I'm trying to do is "lock" the window object and prevent any addition to it (e.g. var foo = 'foo'; in global scope or window['foo'] = 'foo';) before the page scripts runs but after the libraries have loaded.
I already looked at Object.freeze, Object.seal but they don't work for the window object (they raise a TypeError: can't change object's extensibility exception).
I also looked at "cleanup" scripts like these:
// Include this after the libraries.
(function(window) {
var cache = {};
for (var key in window) cache[key] = key;
window.cache = cache;
})(window);
// Include this after all scripts.
(function(window) {
var taint = {};
for (var key in window) taint[key] = key;
for (var key in window.cache) = delete taint[key];
for (var key in taint) delete window[key];
})(window);
But they only clean-up the global scope, they don't prevent the pollution in the first space.
Is it possible to do it? We don't care if the solution breaks the JavaScript code polluting the global scope, we'll consider it a plus since it will force the developer to follow best practices.
P.s. Yes, I do know the best solution would be code reviews, sadly they're not feasible in our situation so we're looking for a catch-all solution.
To lock global variables from the var keyword, wrap your code in an anonymous closure.
(function(){
// ... Your code ...
}();
You can prevent assignment to window in chrome with the following.
Object.preventExtensions(window)

Build my library in JavaScript/Dojo

I will appropriate your help in creating my library in JavaScript and using Dojo.
It seems to me like a beginner problem but I can't seem to find the problem.
I am trying to create a library that is named 'edm'
The EDM.js is very simple:
define([
"dojo/_base/declare"
], function(declare) {
var EDM = declare("EDM", {
constructor: function() {
console.log("Hi EDM lib")
},
sayHi: function() {
console.log("Hi EDM lib")
}
});
edm = new EDM();
return edm;
});
The index.html to load it:
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/dojo/1.9.2/dojo/dojo.js"
data-dojo-config="async: true"></script>
<script src="../src/EDM.js" type="text/javascript"></script>
</head>
<body>
<script src="main.js" type="text/javascript"></script>
</body>
</html>
and the main.js to use the edm is:
var newObject = edm;
The idea is basically to be able to use edm as prefix for the functions in EDM.js file.
However, when loading the index.htm file I get this error: "Uncaught ReferenceError: edm is not defined"
I will appropriate any direction.
Thanks
You should use the Dojo AMD loader in stead of loading the file yourself. Remove the following <script> tag:
<script src="../src/EDM.js" type="text/javascript"></script>
Then write the following code in main.js:
require([ "../src/EDM" ], function(edm) {
var newObject = edm;
});
The advantages of doing it this way is the following:
No need to add <script> tags that slow down the page when loading
No additional objects are added to the global scope
Of course, because no objects are on global scope, you cannot retrieve the edm object directly. That's why you got that ReferenceError.
To answer your question from the comments: yes and no. Actually, you're already adding edm to the global scope in EDM.js because you didn't put var in front of the following statement:
edm = new EDM();
However, the define() callback is only executed if it's called from the Dojo AMD loader inside a require() function. So that actually means that you have to load the AMD module first and then you will be able to access edm from global scope.
So in your case, the following will work as well:
require([ "../src/EDM" ], function(bla) {
var newObject = edm;
});
As you can see in the example above, we call our module bla, however, because you added it to global scope in EDM.js, it's now available. But, in order to add it to the global scope you have to properly load the module.
But be careful, adding objects to global scope is usually not encouraged, because they can be manipulated from any code which makes it hard to debug. There are a lot of other reasons why global variables are not a good idea, you can probably find a lot more information about it on the web.

Categories