I have a website structure
/
/users
/users/wishes
I am using ExpressJS, Jade, and vanilla JS for front-end with 2 separate js files using window.onload function to set up my onclicks. I am expecting that my clients first open / and after this they proceed to the other pages. I have 3 views for each of the pages. Since the website is pretty small I decided not to do overkill by using a JS Framework.
Now I want to minimize my JS files inside a single one, as usual, and load everything in the root. However, it does not seem to work. How should I approach this problem?
You can listen for event instead of replacing window.onload function.
window.addEventListener('load', function() {
console.log('here you go !');
})
But take note, that if listener will be added after event fires, it wont execute.
Related
In my app I have around 10 different pages, and most of them use some sort of JavaScript. Currently, I have one client-side app.js file that is included in all pages. In order to figure out what event listeners to attach on a page I basically check url location and go from there:
window.onload = function () {
let urlLocation = window.location.pathname.split('/')[1]
//Global functionality for all pages
UIctrl.toggleActiveNavbar(urlLocation)
//createTopic route
if (urlLocation === 'createTopic' || urlLocation === 'edit') {
UIctrl.createTopicTagsBasedOnCategory()
UIctrl.handleCreateOrEditTopicClick()
}
if (urlLocation === 'editPost' || urlLocation === 'createPost') {
UIctrl.handleCreateOrEditPostClick()
}
// .... and so on
}
Even though it works, I don't think it's a good way to do it. If a project becomes big enough, it's very hard to manage it.
I was not able to find an answer how to do it properly. My questions are :
Should I have separate js files for each page ? My problem with this is that I have to duplicate common code that is used on all pages.
Do you use some sort of bundlers (webpack/parcel) in your express apps that solve this issue? Maybe you could point me to a repository that shows how to set it up correctly.
How is this done in the real world production environments ?
Thank you.
Your intuition that comparing URLs to decide what initialization to run is a bad way to code is correct. That is not a good pattern and will quickly get out of control with more and more pages and maintenance over time can get to be a real pain.
Instead, you can put common code in a shared JS file that every page loads so those functions are available as needed. Then, use an inline <script> tag inside each individual page to do the page-specific initialization that sets up event listeners that are particular to that page and calls code in the shared JS file.
If for some pages, you have lots of page-specific initialization code, you could put just that page-specific code in a page-specific JS file, but in general you don't want to have an external JS file for every one of your pages if you can avoid it. Try putting most of the code in the common JS file and then just using a small bit of inline code in each specific page to do the right initialization. This puts most of your code in a common, shared JS file and keeps page-specific initialization logic in the specific page.
Should I have separate js files for each page ? My problem with this is that I have to duplicate common code that is used on all pages.
No. You don't want to duplicate lots of code in separate JS files as that is a maintenance nightmare and defeats effective browsing caching.
Is there a way to trigger an event when file is created in some directory? Similar to jquery click function, I want to have something like:
$(document).ready(function () {
$(this).fileCreated(function() {....}
});
I'll need to react to RF card reader, which will store data to file, and this solution (if it is possible ofc) would probably be the easiest to implement.
I'll need this for a web service whivh will only run locally with no access to source code or anything. Security shouldnt be a problem.
#t.niese Pointed out my bad solution.
After reconsidering the issue I would likely have an on click listener in which the callback checked to see if the file path for the new file was to the directory of concern before doing anything else. That way you could also make functions for future paths using the same listener and callback function if you wanted.
Recognizing you want the listener to focus on actual file creation I double checked on JS events and found onsubmit as the closest to what you're looking for, but it appears to simply work similarly to on click anyway.
https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onsubmit
If a request is being made by your application after reading an RF card I don't see why you couldn't use .ready if you incorporate it into some html response.
The only other possibility I can see would be to find some framework that expands on available events. I know Backbone.js does some extra stuff with events.
I am using the Ratchet.js/push.js library to create the UI for a mobile web app. In this library, links are handled by "pushing" the to-be-loaded file into the ".content" DOM element rather than loading the entire page. However, push.js does not load any scripts it finds when loading a page - which disables my Knockout.js code.
I found a solution here on StackOverflow that works pretty well - just add an event listener for the push event. I modified it so that it can load any script across multiple pages and so it works with external script files:
window.addEventListener('push', function () {
var scriptsList = document.querySelectorAll('script.js-custom'); // Add a "js-custom" class to your script tag
for (var i = 0; i < scriptsList.length; ++i) {
// Handle scripts in separate files by assigning the script file name to its id.
// We save it in a variable because the ".done" callback is asynchronous.
scriptName = scriptsList[i].id; // IMPORTANT: Only one loadable script per page!
$.getScript("/path info here/" + scriptName)
.done(function (script, textStatus) {
eval(script);
})
... error handling ...
}
});
In the target HTML page, scripts are given class and id tags so they work with the above:
<script src="Challenge.js" class="js-custom" id="challenge.js"></script>
Note, too, that Knockout bindings have to occur to a specific named DOM element so that knockout doesn't get confused:
ko.cleanNode($("#ChallengePage")[0]);
ko.applyBindings(challengeFn, $("#ChallengePage")[0]);
We use cleanNode to avoid the "already bound" errors.
OK! So all this works great and I hope that someone who is struggling with this finds it useful.
HOWEVER, when the link is given a transition:
<a href="challenge.html" data-transition="slide-in">....
Then it breaks with a "Cannot read property 'nodeType' of undefined. I had thought that maybe it was just a problem of waiting for the transition to finish, but even if I replace the eval of the script with:
scriptContents = script;
setTimeout(function () { eval(scriptContents); }, 1000);
it doesn't help.
Any advice or assistance would be greatly appreciated! I don't really need to "push" pages if I don't get to use the transitions so I am hoping that someone will have the last key to making this all work!
UPDATE: The error was occurring because the "document.querySelectorAll" call when using a transition uses the current document rather than the document being pushed. Also, using "webkitTransitionEnd" as my event works as well but this doesn't fix the document issue. Thus, I can make this work, but only for a single transition - now I don't have a way of getting the document being loaded. Ideally, a solution that works whether a links uses a transition or not is what I am looking for.
The combination of Ratchet and Knockout will likely be popular in the coming months so I hope that others find this solution.
To combine the Ratchet.js and Knockout.js libraries requires only that you handle the fact that Ratchet.js (via Push.js) will attempt to manage your page transitions. During a transition, the JavaScript on your target page - including Knockout - will not be run unless you specifically make this happen. That is what this solution does: it makes it possible to load and run your Knockout JavaScript code even though Ratchet is managing page transitions.
In my solution, we always place JavaScript in a separate file and implement Content Security Policy that forbids any JS code from running on the page. It is simply good security hygiene and helps reduce the attack surface for XSS attacks. So the solution below 1) assumes that the JS is in a separate file and 2) assumes that the HTML and JS files have the exact same name and path - except for the extensions (sort of like treating the .js file like an ASP.NET code-behind for the HTML file).
On your "root" page - the one that starts all of your interactions with other pages on your mobile web app, place the following function. It will load the appropriate .js file whenever the corresponding .html file is loaded by Ratchet:
window.addEventListener('push', function (params) {
var targetPage = params.target.document.baseURI.replace(".html", ".js");
$.getScript(targetPage)
.done(function (script, textStatus) {
eval(script);
})
.fail(function (jqxhr, settings, exception) {
alert("Error loading script: " + exception);
});
});
Note that you will have to apply your Knockout bindings to a named and unique div in your HTML page (generally a div that lives directly underneath the Ratchet .content div). This is just because each page load has to apply its Knockout bindings to just the HTML being loaded.
ko.cleanNode($("#DivPageName")[0]);
ko.applyBindings(KnockoutFn, $("#DivPageName")[0]);
UPDATE: I have found that this solution gets "confused" at times as pages are pushed and popped from the history stack. I have decided not to use it although it seems like it is about 97% there. If anyone has any improvements that would make this completely reliable, I am all ears!
I have some simple JQuery / Javascript to perform some simple logic for all external hyperlinks:
<script>
$("a[href^='http://']:not([href*='"+location.hostname+"']),[href^='https://']:not([href*='"+location.hostname+"'])")
.addClass("external")
.attr("target","_blank")
.attr("title","Opens new window").click(function(e) {alert('You are leaving mysite and going somewhere else, you crazy dude')});
</script>
This is fine for one page. However, I wish to have this in every web page in my application and be 100% sure that it is there.
Is there any good trick to do this?
The only one I can think of is if you are using a java architecture, to have a base JSP and ensure the base JSP calls this.
Any better ideas?
You don't need some server side framework... If you use some templating library (jade handlebars, mustache, jquery templates) or if you simply separate out your HTML files you can pull them each in with jquery and render them on the page. Check out the .load function.
Also, you should separate out your html pages even if they are static.
Wrap it in a function and call the function. Then you can just call the function and leave the implementation to the function call.
Since you didn't specify a server side technology such asp.net or php of course there would be other options (partial views or templates) using that.
function doStuff(){
$("a[href^='http://']:not([href*='"+location.hostname+"']),
[href^='https://']:not([href*='"+location.hostname+"'])")
.addClass("external")
.attr("target","_blank")
.attr("title","Opens new window").click(function(e) {alert('You are leaving mysite and going somewhere else, you crazy dude')});
}
<script>
doStuff();
</script>
This depends on how your site is structured. If you've got a server-side framework (like your JSP example), then you can have a function that makes sure that the script somehow gets included.
If you just have static HTML pages, my recommendation would be to put that code in a script file (let's say dontleaveme.js). Then on every page, just do
<script src="dontleaveme.js"></script>
A good application design will have a layout file or header and footer files which are used on every page. Then it is easy to make changes, such as adding a script, which affect every page on the site. If you are not using this technique, this is a great reason to start.
I want to run the following jquery code on every page in my website.
$(document).ready(function(){
$("#more").click(function(){
$("#morediv").slideToggle("slow");
return false;
});
});
In all my pages I have the more and morediv elements defined, for every page I have different js file and adding this code in every file will not be a good solution (I suppose).
I have created a global.js to include this code, but in other pages also I have the $(document).ready(function(){} function defined and may be that's why its conflicting and not running properly.
You can have multiple $(document).ready(function(){}) elements on your page, so that it's the problem. I suggest using Firefox/Firebug and examining any console errors you find to discover the problem. Perhaps your global.js file is being loaded before jQuery itself? Otherwise, you'll need to dig into it with Firebug's debugger.
Are you actually doing some server-side programming or you are talking about plain HTML pages. I would advise that you have templates (this is specific to your development environment and tools of choice) and include the JS in those templates. Then the actual pages will all use the template and have the JS available. The question you are asking has in fact nothing to do with Javascript or JQuery, but the way you organize your site... unless I'm missing something.
having $(document).ready() event handler in global.js and the page it is included in does not poses any problem I'm using it and it works really fine.
Just a guess, but are you referencing the location of the global.js file correctly?
To be sure, write something like the following into your global script:
$(document).ready(function(){
alert("document ready");
$("#more").click(function(){
$("#morediv").slideToggle("slow");
return false;
});
});
If you don't get the alert the script is not pathed correctly, or is not placed after the jquery include (or the jquery include is not pathed properly).