├── src ├── views │ ├── detail.html │ └── landing.html ├── index.html ├── index.js └── libs │ └── router.js ├── dist ├── .vscode │ └── settings.json ├── index.html └── main.js ├── designs ├── detail.jpg └── landing.jpg ├── ASSUMPTIONS.md ├── README.md ├── package.json └── webpack.config.js /src/views/detail.html: -------------------------------------------------------------------------------- 1 | details page -------------------------------------------------------------------------------- /src/views/landing.html: -------------------------------------------------------------------------------- 1 | landing page -------------------------------------------------------------------------------- /dist/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "liveServer.settings.port": 5502 3 | } -------------------------------------------------------------------------------- /designs/detail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitinhepat/spa-without-framework/HEAD/designs/detail.jpg -------------------------------------------------------------------------------- /designs/landing.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nitinhepat/spa-without-framework/HEAD/designs/landing.jpg -------------------------------------------------------------------------------- /ASSUMPTIONS.md: -------------------------------------------------------------------------------- 1 | # Assumptions 2 | 3 | [LIST ASSUMPTIONS HERE] 4 | 5 | # Still to do 6 | 7 | unable to implement designs because of less time 8 | 9 | [LIST WHAT IS STILL TO DO HERE] 10 | -------------------------------------------------------------------------------- /dist/index.html: -------------------------------------------------------------------------------- 1 | Details Landing
-------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Details 9 | Landing 10 |
11 | 12 |
13 | 14 | 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ** steps for running ** 5 | 1. open command line 6 | 2. npm install 7 | 3. run 'npm start' command for dev enviroment 8 | 4. run 'npm run build' for creating build 9 | 10 | For more explanation 11 | Please go through this link 12 | 13 | https://techboxweb.com/build-a-javascript-single-page-application-without-framework/ 14 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import {Router,Routes} from './libs/router.js' 2 | 3 | 4 | (()=>{ 5 | const routeConfig = [ 6 | new Routes({ 7 | path:'detail', 8 | url: './views/src/views/detail.html' 9 | }), 10 | new Routes({ 11 | path:'landing', 12 | url: './views/src/views/landing.html' 13 | },true)] 14 | 15 | new Router(routeConfig,'app'); 16 | })() -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "spa", 3 | "version": "1.0.0", 4 | "description": "A single-page application (SPA) is a web application or website that interacts with the web browser by dynamically rewriting the current web page with new data from the web server, instead of the default method of the browser loading entire new pages.", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "webpack", 8 | "start": "webpack-dev-server" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "devDependencies": { 13 | "copy-webpack-plugin": "^5.1.1", 14 | "css-loader": "^3.4.2", 15 | "file-loader": "^5.1.0", 16 | "html-loader": "^0.5.5", 17 | "html-webpack-plugin": "^3.2.0", 18 | "mini-css-extract-plugin": "^0.9.0", 19 | "sass-loader": "^8.0.2", 20 | "style-loader": "^1.1.3", 21 | "webpack": "^4.41.6", 22 | "webpack-cli": "^3.3.11", 23 | "webpack-dev-server": "^3.10.3" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 2 | const CopyPlugin = require('copy-webpack-plugin'); 3 | 4 | module.exports = { 5 | module : { 6 | rules : [ 7 | { 8 | test: /\.html$/, 9 | use: { 10 | loader: 'html-loader', 11 | options: {minimize: true} 12 | } 13 | }, 14 | { 15 | test: /\.(png|svg|gif|jpg)$/, 16 | use: { 17 | loader: 'html-loader', 18 | options: {minimize: true} 19 | } 20 | }, 21 | { 22 | test: /\.scss$/, 23 | use: ['css-loader','style-loader','sass-loader'] 24 | }, 25 | 26 | ] 27 | }, 28 | plugins: [ 29 | new HtmlWebpackPlugin({ 30 | template: './src/index.html', 31 | filename: './index.html' 32 | }), 33 | new CopyPlugin( [{ from: './src/views/*', to: './views' }] 34 | ) 35 | ] 36 | } -------------------------------------------------------------------------------- /dist/main.js: -------------------------------------------------------------------------------- 1 | !function(t){var e={};function n(r){if(e[r])return e[r].exports;var i=e[r]={i:r,l:!1,exports:{}};return t[r].call(i.exports,i,i.exports,n),i.l=!0,i.exports}n.m=t,n.c=e,n.d=function(t,e,r){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var i in t)n.d(r,i,function(e){return t[e]}.bind(null,i));return r},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="",n(n.s=0)}([function(t,e,n){"use strict";n.r(e);class r{constructor(t,e){this.routes=t,this.routeElement=e,this.initialize()}getPathAndRouteMapping(){const t={};for(let e in this.routes)t[this.routes[e].viewObj.path]=this.routes[e].viewObj.url;return t}initialize(){window.addEventListener("hashchange",t=>{this.hashChanged()})}hashChanged(){const t=window.location.hash;for(let e=0;e{4===this.readyState&&200===this.status&&(this.routeElement.innerHTML=this.responseText)},n.open("GET",e,!0),n.send()}}class i{constructor(t,e){this.viewObj=t,this.isDefaultRoute=e}isActiveRoute(t){return t.replace("#","")===this.viewObj.path}}(()=>{const t=[new i({path:"detail",url:"./views/detail.html"}),new i({path:"landing",url:"./views/landing.html"},!0)];new r(t,"app")})()}]); -------------------------------------------------------------------------------- /src/libs/router.js: -------------------------------------------------------------------------------- 1 | export class Router { 2 | 3 | constructor(routes, routeElement) { 4 | this.routes = routes; 5 | this.routeElement = document.getElementById(routeElement); 6 | this.initialize(); 7 | this.hashChanged(); 8 | } 9 | getPathAndRouteMapping() { 10 | const routeMapping = {}; 11 | for (let objKey in this.routes) { 12 | routeMapping[this.routes[objKey].viewObj.path] = this.routes[objKey].viewObj.url; 13 | } 14 | return routeMapping; 15 | } 16 | initialize() { 17 | window.addEventListener('hashchange', (e) => { 18 | this.hashChanged() 19 | }) 20 | } 21 | 22 | hashChanged() { 23 | const locationHash = window.location.hash; 24 | for (let i = 0; i < this.routes.length; i++) { 25 | const route = this.routes[i]; 26 | if (route.isActiveRoute(locationHash.substr(1))) { 27 | this.navigate(route.viewObj.path) 28 | } 29 | } 30 | } 31 | 32 | navigate(path) { 33 | const pathRouteMapping = this.getPathAndRouteMapping(); 34 | const url = pathRouteMapping[path]; 35 | const xhttp = new XMLHttpRequest(); 36 | let scope = this; 37 | xhttp.onreadystatechange = function() { 38 | if (this.readyState === 4 && this.status === 200) { 39 | scope.routeElement.innerHTML = this.responseText; 40 | } 41 | }; 42 | xhttp.open('GET', url, true); 43 | xhttp.send(); 44 | } 45 | 46 | } 47 | 48 | export class Routes { 49 | constructor(viewObj, isDefaultRoute) { 50 | this.viewObj = viewObj; 51 | this.isDefaultRoute = isDefaultRoute; 52 | } 53 | isActiveRoute(hashPath) { 54 | return hashPath.replace('#', '') === this.viewObj.path 55 | } 56 | } 57 | 58 | --------------------------------------------------------------------------------