Yabble - accessing functions loaded by yabble - javascript

Am having a problem with Yabble that I have not been able to solve.
From within my main html I load all of my js using yabble
<script>
require.setModuleRoot('./javascript/');
require.run('main')
</script>
I have a bunch of js (gamejs related). In one file I have function changeSimulationSettings(). Later within the same page I want to take user input and access the gamejs objects and change their state. The problem I am having is that I am unable to call changeSimulationSettings. It is not defined within the current context. Yabble does so much magic I am unable to find where it is defined or how to access.
<div>
<button type="button" onclick="updateSettings()">Update-Settings</button>
</div>
<script type="text/javascript">
function updateSettings(){
// access function defined in a js loaded by yabble
i.e. changeSimulationSettings()
}
</script>
All of the js is definitely being loaded as I have got functions calling each other from different files which does work. However, I am unable to call anything from js embedded within the entry web page.
Any help would be appreciated as I have been stuck on this one for hours and hours.
Thanks,

Code defined in modules is isolated and not available globally.
I would do it the other way around: from within a module attach an event handler. Do not use "onclick=" but instead in a module, where you have changeSimuSettings available, do something like this (e.g. using jquery or plain DOM):
$('button').click(changeSimulationSettings)
This is cleaner and you are not leaking globals. If you insist on doing it your way, you could export changeSimulationSettings to global and use the code you already have. Like so (I would not recommend that):
window.changeSimulationSettings = function() {...

Related

Question about functions being called from other files - Javascript

I am looking for some help understanding something on a project I am working on. I have written some code that is functioning, though I am not sure why.
In a Node.js server, in /public/js there are two scripts. One (file1.js) has a function func(). file2.js, in the same directory, calls func() successfully. There is no module.exporting or requireing anywhere, yet the two files work together. They are both referenced in the index.ejs file, however. Is this where they become able to communicate?
//file1.js
function func() {
console.log("foo")
}
//file2.js
func()
//index.ejs
...
<script src="public/js/file1.js"></script>
<script src="public/js/file2.js"></script>
...
I've spent all day reading and cannot find anything on this topic.
Your question is about how JavaScript works in a browser.
Node.js isn't relevant here. All it is doing is running an HTTP server program that gives static files to the browser.
When you load a script into a browser using a script element (and don't use type="module"), any variable in the outer most scope of the script file (e.g. which isn't let inside a block or var inside a function) becomes a global and is accessible to any other script loaded into the same HTML document that way.
Globals are messy and a good way for different bits of code to accidentally interfere with each other so modern JavaScript generally avoids using them. This style of JavaScript programming wasn't common when JS was first implemented in browsers: Hence the behaviour described above.
This is why people started to use techniques like the revealing module pattern and why AMD and Node modules were designed before standard JavaScript modules were added to the specification.
You must understand how the global space behaves in Javascript.
This Code:
<script src="public/js/file1.js"></script>
<script src="public/js/file2.js"></script>
Is the same as this:
<script>
//file1.js
function func() {
console.log("foo");
}
func();
</script>
Because soon as file1.js is loaded, everything that is defined inside of it, becomes available anywhere in that page from where it was included.
Since file2.js uses contents of file1.js, it will work because func is available to be used anywhere below file1.js inclusion.

Google Tag Manager: Delete custom HTML blocks after each pageview?

We have a single-page-application with Google Tag Manager setup, and a working usage of the custom HTML fragment for a tracking library to fire a JS method for every page view. Though it works - it leaves behind HTML script fragments for each page view.
My question: Is there some existing functionality that will delete the Custom HTML blocks?
Or should would I have to write custom code in the same script to clear up old versions of the same snippet?
Custom HTML snippet for per-page tracking:
<script type="text/javascript">
someTrackingLibrary.pageViewEvent();
</script>
Note: The use of the custom script block is from some advice given to us, and kinda mentioned in this GTM documentation:
The third, unwritten rule is that the function should only return a
value. You shouldn’t use a Custom JavaScript variable to modify the
global namescape by pushing values to dataLayer for example. If you
want to tamper with global variables from a function, it’s better to
create a Custom HTML tag for this purpose.
And is suggested to not be done with a variable:
Variables must never have side effects
Remember how variables should only be used to return values? Well,
sometimes you might be tempted to use a variable to change the state
of the global object or to set or push stuff into dataLayer. Don’t do
it! Because variables can be resolved in multiple ways, and not just
in tags as you’d expect, you might find yourself creating infinite
loops, pushing stuff into dataLayer multiple times, or severely
hurting page performance.
A few things:
Do not use variables to make create/delete operations: you do not control when variables are executed (you can create a dummy JS variable with console output, you'll be surprised how often and seemingly randomly it is called), thus using variables for modifying operations is a very bad idea
There is no built-in GTM functionality to achieve what you want
You can create a custom tag to do it: see below example. The code logic is that at the time of its execution, a <script> block is the last script available in the stack (logically since the browser just parsed it), therefore it's pretty easy to retrieve it. You could add a dataLayer.push call at the end of your script blocks to to notify GTM that the script has been executed.
If you try to implementing a solution without the dataLayer.push
method, you'll have a race condition with the risk of GTM deleting the
script tags before they have been executed (since GTM is loading async
and might be ready before browser parses those script blocks).
Below is the sample page code:
<script>
var removeBlock = function() {
var scriptTag = document.scripts[document.scripts.length - 1];
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
event: "delete_block",
block: scriptTag
});
}
</script>
<script id="block1">
console.log("block1");
</script>
<script id="block2" type="text/javascript">
// YOUR ANALYTICS CODE HERE BEFORE GTM PUSH
console.log("block2");
removeBlock();
</script>
<script id="block3">
console.log("block3");
</script>
Then create a trigger based on event=delete_block and here is the GTM Custom HTML tag code:
<script type="text/javascript">
var block = {{block}}; // GTM dataLayer variable "block"
block.parentNode.removeChild(block);
</script>
Is there some existing functionality that will delete the Custom HTML blocks?
No. GTM is a tag manager, you need to write your custom code to delete the code blocks.
I would suggest you doing it via your 3rd party library if that has some functionality to do so. Else you can write custom JS or add a tag like wise.
As a belated answer, do not use custom HTML tags anymore - for most intents and purposes, they have been superseded by custom templates.
Custom templates allow to define an UI (e.g. to specify account ids or whatever input is necessary for your tag) and a code block. The only issue (and that's a good issue, even if it's annoying sometimes) is that you are limited to what Google calls "sandboxed javascript". I.e. you can not use all features of standard JS, you have to go through APIS provided by Google - a "good issue" because it offers extra levels of security, however some things (that are insecure by nature) do not work with sandboxed Javascript (e.g. no modifying the prototype chain for objects, no direct DOM access and some other limitations).
However they avoid the problem of overpopulating the DOM with copies of your custom tag (and for your example, you can call functions that are defined on the global namespace).

Why is jQuery document ready closing off access to global variable?

I have a project where I have three separate JavaScript files (example1.js, example2.js and example3.js lets call them) scripted into one html file (index.html). These three JS files between them are responsible for an API call and manipulating the result.
One of the JS files, example1.js refers to a global variable located in example2.js and as they were both loading into the same html document I thought the access to said variable wouldn't be an issue and it did indeed work perfectly fine until I added the files to my RoR app. Due to Rails I had to encase the JS/jQuery code inside of $document.ready(function(){}
(I should probably should do this as a matter of course anyway?).
The effect this has had is that I am now getting a 'variable not defined' error on the global variable referred to by example1.js that is located in example2.js, even though other code in the same file is working correctly.
I went back to my original JS files away from RoR incase it was a Rails issue. However, encasing the code in my original files with the jQuery document.ready function has the same effect outside of the Rails environment. Can someone explain why this is and a possible solution?
I completely overlooked the fact that document.ready is itself a function and therefore removed everything from the global scope into the function scope of document.ready.

external js-files don't know each other

I have a very long external JS-file which I want to split in 2 seperate JS-files. The problem with this is, that file_1.js doesn't know the functions of file_2.js anymore. Is there something special I don't have in mind when I'm doing this.
<script src="js/file_1.js"></script>
<script src="js/file_2.js"></script>
head of my html.
and I'm loading every content in a document ready.
$(function() { some code in both });
Cheers
Javascript files can only access code from files that are loaded before them. In this example file2 can access functions in file1, but not the other way around.
If they each need to access each other, you have a circular dependency. When this happens, it usually means your two files should really just be one big one.
As previously stated, calling the external js file which declares the functions before the external js which calls the functions is what you need to do.
Have you tried calling one of the functions from within the dom? If that fails as well, there may be issues with how you broke up the js.
<script src="js/file_2.js"></script>
<script src="js/file_1.js"></script>
vs
<script src="js/file_2.js"></script>
<script>
$(document).ready(function(){
someFunctionWithinFile_2();
});
</script>

I have an issue with inline vs included Javascript

I am relatively new to JavaScript and am trying to understand how to use it correctly.
If I wrap JavaScript code in an anonymous function to avoid making variables public the functions within the JavaScript are not available from within the html that includes the JavaScript.
On initially loading the page the JavaScript loads and is executed but on subsequent reloads of the page the JavaScript code does not go through the execution process again. Specifically there is an ajax call using httprequest to get that from a PHP file and passes the returned data to a callback function that in onsuccess processes the data, if I could call the function that does the httprequest from within the html in a
<script type="text/javascript" ></script>
block on each page load I'd be all set - as it is I have to inject the entire JavaScript code into that block to get it to work on page load, hoping someone can educate me.
If you aren't using a javascript framework, I strongly suggest it. I use MooTools, but there are many others that are very solid (Prototype, YUI, jQuery, etc). These include methods for attaching functionality to the DomReady event. The problem with:
window.onload = function(){...};
is that you can only ever have one function attached to that event (subsequent assignments will overwrite this one).
Frameworks provide more appropriate methods for doing this. For example, in MooTools:
window.addEvent('domready', function(){...});
Finally, there are other ways to avoid polluting the global namespace. Just namespacing your own code (mySite.foo = function...) will help you avoid any potential conflicts.
One more thing. I'm not 100% sure from your comment that the problem you have is specific to the page load event. Are you saying that the code needs to be executed when the ajax returns as well? Please edit your question if this is the case.
I'd suggest just doing window.onload:
<script type="text/javascript">
(function() {
var private = "private var";
window.onload = function() {
console.log(private);
}
})();
</script>
On initially loading the page the js loads and is executed but on subsequent reloads of the page the js code does not go through the execution process again
I'm not sure I understand your problem exactly, since the JS should execute every time, no matter if it's an include, or inline script. But I'm wondering if your problem somehow relates to browser caching. There may be two separate points of caching issues:
Your javascript include is being cached, and you are attempting to serve dynamically generated or recently edited javascript from this include.
Your ajax request is being cached.
You should be able to avoid caching by setting response headers on the server.
Also, this page describes another way to get around caching issues from ajax requests.
It might be best not to wrap everything in an anonymous function and just hope that it is executed. You could name the function, and put its name in the body tag's onload handler. This should ensure that it's run each time the page is loaded.
Depends what you want to do, but to avoid polluting the global namespace, you could attach your code to the element you care about.
e.g.
<div id="special">Hello World!</div>
<script>
(function(){
var foo = document.getElementById('special');
foo.mySpecialMethod = function(otherID, newData){
var bar = document.getElementById(otherID);
bar.innerHTML = newData;
};
//do some ajax... set callback to call "special" method above...
doAJAX(url, 'get', foo.mySpecialMethod);
})();
</script>
I'm not sure if this would solve your issue or not, but its one way to handle it.

Categories

Resources