Reputation:
I am making Vanilla JS SPA - APP - and all works fine beside that when a View is loaded in the "app" container the html is loaded fine but javascript under the html is not executed. Is there a way to make JS script under the html execute? There are different scripts for every view so it could be good if it was possible to load them together with the relevant html for the specific View.
Regards
Register VIEW:
// Imports
// Using the abstract class
import AbstractView from "./AbstractView.js";
// Class ###########################################################>
export default class extends AbstractView {
// Constructor =================================================>
constructor() {
super(); // The abstract class Constructor "Base constructor"
this.setTitle("Register");
}
// Get Html ====================================================>
async getHtml() {
return `
<form id="registerForm">
<div name="register" class="inputContainer">
<h4 class="title">Register</h4>
<hr class="hrTitle">
<diV class="colom">
<div class="form-group">
<label for="email">Email:</label>
<input name="email" type="text" maxlength="25" id="email" class="form-control inputDark" />
<label id="emailValidation"></label>
</div>
<div class="form-group">
<label for="password">Password:</label>
<input name="password" type="text" maxlength="40" id="password" class="form-control inputDark" />
<label id="passwordValidation"></label>
</div>
<div class="form-group">
<label for="repeatPassword">Repeat Password:</label>
<input type="text" maxlength="40" id="repeatPassword" class="form-control inputDark" />
<label id="repeatPasswordValidation"></label>
</div>
<div class="form-group">
<label for="firstname">FirstName:</label>
<input name="firstname" type="text" maxlength="20" id="firstname" class="form-control inputDark" />
<label id="firstNameValidation"></label>
</div>
<div class="form-group">
<label for="lastname">LastName:</label>
<input name="lastname" type="text" maxlength="20" id="lastname" class="form-control inputDark" />
<label id="lastNameValidation"></label>
</div>
<div class="form-group">
<label for="age">Age:</label>
<input name="age" type="text" maxlength="3" id="age" class="form-control inputDark" />
<label is="ageValidation"></label>
</div>
<div class="form-group">
<label for="phonenumber">Phonenumber:</label>
<input name="phonenumber" type="text" maxlength="8" id="phonenumber" class="form-control inputDark" />
<label id="phonenumberValidation"></label>
</div>
<div class="form-group">
<label for="rememberMe" class="subTitle" >Remember Me:</label>
<input name="rememberMe" value="true" type="checkbox" id="rememberMe" class="form-control inputDark" />
</div>
</div>
<button type="submit" onkeypress="javascript:registerAccount()" onmousedown="javascript:registerAccount(); " class="blue-dark-button">Enter</button>
</div>
</form>
<script>(function(){alert('this happened automatically');})();>/script> // This script does not loads on innerHtml insertion.
`;
}
Index.js - Routing and insertion of HTML for the views:
Its at the bottom document.querySelector("#app").innerHTML = await view.getHtml();
// Imports
import Dashboard from "./views/Dashboard.js";
import Posts from "./views/Posts.js";
import Settings from "./views/Settings.js";
import Register from "./views/Register.js";
// Navigator--------------------------------------------------------------------------------->
const navigateTo = url => {
history.pushState(null, null, url); // Add the url to the history APi of Js
router();
};
// Router------------------------------------------------------------------------------------>
const router = async () => {
const routes = [
{path: "/", view: Dashboard}, // On Path "/" use the dashboard class and inject html in the #app div
{path: "/posts", view: Posts },
{path: "/settings", view: Settings },
{path: "/Register", view: Register }
];
// Test each route for potential match ----------------------------------------------------->
// Get the current Url and check if its defined in routes method "Check if its one of our Spa Urls" ----------------------------------------------------->
const potentialMatches = routes.map(route => {
return {
route: route,
isMatch: location.pathname === route.path // true if match else false
};
});
// Check if there is Match------------------------------------------------------------------->
let match = potentialMatches.find(potentialMatch => potentialMatch.isMatch); // Get isMatch from potentialMatches
// If no match return to StartPage
if(!match)
{
match = {
route: routes[0],
isMatch: true
};
}
const view = new match.route.view(); // If match use the routes array of the router and get the view function for the route
document.querySelector("#app").innerHTML = await view.getHtml(); // Get the #app div and use the view function to inject Html in it from the view class ex."Dashboard, Posts, Settings etc."
};
// On-Navigating-Back&Forth-Load the Content--Together with the url------------------------------------------------------------------------------------>
window.addEventListener("popstate", router); // On popstate "If back button is pressed" use the router array to load back the previeous SPA View
// Listen to document fully Loaded
document.addEventListener("DOMContentLoaded", () => {
document.body.addEventListener("click", e => { //Listen for click in the body
if(e.target.matches("[data-link]")){ // If body item was clicked and its data-link decorated
e.preventDefault(); // Prevent deafult behavior dont follow the link
navigateTo(e.target.href); // Navigate method
}
});
router(); // Load the content if the url is defined in our "Spa Urls"
});
//#### Client Routing END #####
Index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Leanheat</title>
<link rel="stylesheet" href="/Static/css/index.css">
<link rel="stylesheet" href="Static/css/components.css" />
<script src="Static/js/api/apiAdresses.js"></script>
<script src="Static/js/api/CRUD/register.js"></script>
<script src="Static/js/components/Messages.js"></script>
</head>
<!--BODY------------------------------------------------------------------------------------------------------>
<body>
<div class="wrapper-1">
<!--SideBar - LEFT ------------------------->
<div id="left-bar" class="left-bar">
<a href="/" class="left-bar__link" onclick="" data-link>Dashboard</a>
<a href="/posts" class="left-bar__link" data-link>Posts</a>
<a href="/settings" class="left-bar__link" data-link>Settings</a>
</div>
<!--<div class="sucsess-Message"><p> SUCCESS</p></div>-->
<div class="wrapper-2">
<div class="top-bar">
<a href="/Register" onclick="" data-link>Register</a>
<a href="/LogIn">Log In</a>
<a href="/LogOut" >Log Out</a>
</div>
<!--APP - Content ------------------------>
<div id="app"></div>
</div>
</div>
<!--SPA - JS - Routing ------------------->
<script type="module" src="/Static/js/index.js"></script>
</body>
</html>
Upvotes: 0
Views: 2357
Reputation:
I found the solution - I have the specific Views - "pages" - which are using abstract View.
The abstract view has the getHtml - method and the Client router is the one that inserts the selected view into the <div id="app"></div>
- the view container. I just needed to add another method to the abstract View - "async executeViewScript(){}
" and add it to all other views that are based on the abstract View, so I now just can call the "async executeViewScript(){}
" after the async getHtml(){}
in the Client router.
Abstract View:
// Class ###########################################################>
export default class{
// Constructor =================================================>
constructor()
{
}
// Set Title ====================================================>
setTitle(title) {
document.title = title;
}
// Get Html ====================================================>
async getHtml()
{
return "";
}
// View Script ====================================================>
async executeViewScript() // !!! The fix
{
}
}
Index.js - Client Router:
// Imports
import Dashboard from "./views/Dashboard.js";
import Posts from "./views/Posts.js";
import Settings from "./views/Settings.js";
import Register from "./views/Register.js";
import Login from "./views/Login.js";
import Profile from "./views/Profile.js";
// Navigator--------------------------------------------------------------------------------->
const navigateTo = url => {
history.pushState(null, null, url); // Add the url to the history APi of Js
router();
};
// Router------------------------------------------------------------------------------------>
const router = async () => {
const routes = [
{path: "/", view: Dashboard}, // On Path "/" use the dashboard class and inject html in the #app div
{path: "/posts", view: Posts },
{path: "/settings", view: Settings },
{path: "/Register", view: Register },
{path: "/Login", view: Login },
{path: "/Profile", view: Profile }
];
// Test each route for potential match ----------------------------------------------------->
// Get the current Url and check if its defined in routes method "Check if its one of our Spa Urls" ----------------------------------------------------->
const potentialMatches = routes.map(route => {
return {
route: route,
isMatch: location.pathname === route.path // true if match else false
};
});
// Check if there is Match------------------------------------------------------------------->
let match = potentialMatches.find(potentialMatch => potentialMatch.isMatch); // Get isMatch from potentialMatches
// If no match return to StartPage
if(!match)
{
match = {
route: routes[0],
isMatch: true
};
}
const view = new match.route.view(); // If match use the routes array of the router and get the view function for the route
document.querySelector("#app").innerHTML = await view.getHtml(); // Get the #app div and use the view function to inject Html in it from the view class ex."Dashboard, Posts, Settings etc."
await view.executeViewScript(); // !!! The fix - THIS is the script that executes exactly after the inserted HTML-
};
// On-Navigating-Back&Forth-Load the Content--Together with the url------------------------------------------------------------------------------------>
window.addEventListener("popstate", router); // On popstate "If back button is pressed" use the router array to load back the previeous SPA View
// Listen to document fully Loaded
document.addEventListener("DOMContentLoaded", () => {
document.body.addEventListener("click", e => { //Listen for click in the body
if(e.target.matches("[data-link]")){ // If body item was clicked and its data-link decorated
e.preventDefault(); // Prevent deafult behavior dont follow the link
navigateTo(e.target.href); // Navigate method
}
});
router(); // Load the content if the url is defined in our "Spa Urls"
});
//#### Client Routing END #####
Upvotes: 1
Reputation: 1783
This is a "feature" of JavaScript and AJAX to try and prevent arbitrary code execution. script
tags can only travel one level deep... So if you load an HTML page and that HTML page has a script
tag, that's fine. If that script loads a "sub-HTML" page and that "sub-HTML" page also has a script
tag, that script
tag will be ignored. It'll show up in the DOM and everything but if you look at network traffic, it's never requested and it won't be executed.
There are ways to get around this but nowadays things like this just create a big ole bundle of all the JS files and load them up front so that your sub-HTML doesn't have to send it's script
along with it.
If you really want to do it that way, it's possible using libraries like RequireJS. Make sure you follow instructions for doing this at runtime. Almost all will assume you want to do it at the bundle/compile/package time.
Upvotes: 1