For my CS class, I need to, by the end, have written an entire website. The idea for my site is to supply information about Uromastyces. Because of this, most of each page will be nearly identical. The only part that changes will be the main body that contains unique information (this seems to be common on most large sites. The header and navbar are always the same, but the body can differ).
I first manually wrote up the first 3 pages, then realized there was a good number of changes I wanted to make to the layout, which required my to comb through each page of code, making identical changes everywhere.
Then I learned about DOM manipulation, and I came up with the idea of generating the page from a central template, and filling in a section (the "unique body") with the part of the page that changes.
This is the JS code that I came up with to fill this need. It's in 2 main parts, each represented as an object literal:
SitePieces
: "Generator" functions that create the individual parts of the site (the header, the nav bar, the footer...). Any piece of changeable information is fed in.SiteFactory
: The section that holds all the otherwise "magic" constants that may change, or at least should be easy to change or lookup (class names, and things like the image path for the sidebar image), and puts all the pieces together.
To change the overall structure, SiteFactory
is updated. To change an individual part, the relevant function in SitePieces
is updated.
What I'd like advice on:
- Right now, all my constants are just grouped together at the top of
SiteFactory
in a semi-arbitrary ordering. Is there a neater way to organize the data? - General thoughts on the setup of
SitePieces
andSiteFactory
. - Are my comments good? I'm coming from Java, and tried to use a Javadoc-like notation, but there doesn't seem to be any way to access the comments besides manually checking the source.
- Should I be doing type checks? Say, for the function
generateUniqueBody
, should I be checking thatuniqueBodyDOM
is in fact a DOM object? And should I be using Hungarian-style notation (-"DOM")? I know it's generally frowned upon, but I need to communicate type information somehow. - ANYTHING else. I'm just learning JS now, so any advice about style/idioms/whatever would be appreciated.
What I don't want advice on:
- From what I know about jQuery, I'm pretty sure it could have helped simplify this code a lot. I'm not quite at the point where I want to pick it up though, I'd rather learn JS more in-depth first, therefore I don't need advice to use jQuery.
- I need to tweak the CSS. From the screenshot, you can see that some parts are squished together (the footer). That's beyond the scope of my review request though.
- The fact that this isn't a generalized factory. I'm not (currently) looking to create a general-purpose factory to put sites together, I only need it to build my project's site.
Sample without a unique body:
It's use:
<!DOCTYPE html>
<html lang="en">
<head>
</head>
<body>
<script src="SiteGenerator.js"></script>
<script>
SiteFactory("Test Page", document.createElement('div'), "15.12.13");
</script>
</body>
</html>
SiteGenerator.js:
/**
* Created by Brendon on 2015-12-08.
*/
/*
Constructs the site using the supplied constructor information.
*/
var SiteFactory = function(pageTitle, uniqueBodyDOM, lastModified) {
//The various site tweaks:
var headerClasses = 'mainHeader';
var navLinks = [
new LI('Home', 'index.html'),
new LI('Enclosure', 'enclosure.html'),
new LI('Diet', 'diet.html'),
new LI('Behavior and Life', 'behaviorAndLife.html'),
new LI('About Me', 'aboutMe.html')
];
var navBarClasses = 'navBar';
var sidebarImgPath = 'images/pascal-cropped-shrunk.jpg';
var sidebarImgAlt = 'A Picture of Pascal';
var sidebarImgClasses = 'sideBarImage';
var sidebarClasses = 'mainSideBar';
var uniqueBodyClasses = 'uniqueBody';
var footerClasses = 'mainFooter';
var footerWrapperClasses = 'footerWrappers';
//End of tweaks
SitePieces.appendHeadElements(pageTitle);
var header = SitePieces.generateHeader(headerClasses);
var navBar = SitePieces.generateNavBar(navLinks, navBarClasses);
var sideBar = SitePieces.generateSideBar(navBar, sidebarImgPath, sidebarImgAlt, sidebarImgClasses, sidebarClasses);
var uniqueBodyWrapper = SitePieces.generateUniqueBodyWrapper(uniqueBodyDOM, uniqueBodyClasses);
var footer = SitePieces.generateFooter(lastModified, footerClasses, footerWrapperClasses);
SitePieces.appendChildren(document.body,
[header, sideBar, uniqueBodyWrapper, footer]);
};
/*
Helper functions to generate the individual pieces of the site
*/
var SitePieces = {
/*
Appends the required elements to the pages's head.
pageTitle: The title of the page to be generated.
uniqueStyleSheetPath: A path to a unique style sheet to be used on this page (optional).
*/
appendHeadElements: function(pageTitle, uniqueStyleSheetPath) {
var title = document.createElement('title');
title.innerHTML = pageTitle;
var meta = document.createElement('meta');
meta.charset = 'UTF-8';
var cssLink = this.createCSSLink('styles/styles.css');
this.appendChildren(document.head,
[title, meta, cssLink]);
if (uniqueStyleSheetPath) {
var uniqueCSSLink = this.createCSSLink(uniqueStyleSheetPath);
document.head.appendChild(uniqueCSSLink);
}
},
/*
Generates the page header
headerClass: The classes to assign to the header
*/
generateHeader: function(headerClasses) {
var header = document.createElement('header');
var h1 = document.createElement('h1');
h1.innerHTML = "Brendon's Uromastyces Fact Site";
header.appendChild(h1);
return header;
},
/*
Generates the navbar for the page, populated by the supplied links
links: The links to populate the navbar with.
navClassName: The classes to assign to the navbar.
returns: The generated navbar.
*/
generateNavBar: function(links, navClassNames) {
var nav = document.createElement('nav');
nav.className = navClassNames;
var unordList = document.createElement('ul');
for (var linkI = 0; linkI < links.length; linkI++) {
var linkObject = links[linkI];
var link = document.createElement('a');
link.href = linkObject.linkAddress;
link.innerHTML = linkObject.linkText;
var listItem = document.createElement('li');
listItem.appendChild(link);
unordList.appendChild(listItem);
}
nav.appendChild(unordList);
return nav;
},
/*
Generates the sidebar for the page
navBar: The previously generated navbar to place inside the sidebar.
imgSrc: The source of the image for the sidebar.
imgAlt: A description of the sidebar image.
sidebarImgClasses: The classes to assign to the sidebar image.
sidebarClasses: The classes to assign to the sidebar.
returns: The generated sidebar.
*/
generateSideBar: function(navBar, imgSrc, imgAlt, sideBarImgClasses, sideBarClasses) {
var aside = document.createElement('aside');
aside.className = sideBarClasses;
var image = document.createElement('img');
image.src = imgSrc;
image.alt = imgAlt;
image.className = sideBarImgClasses;
this.appendChildren(aside,
[image, navBar]);
return aside;
},
generateUniqueBodyWrapper: function(uniqueBodyDOM, wrapperClasses) {
var uniqueBodyWrapper = document.createElement('section');
uniqueBodyWrapper.className = 'wrapperClasses';
uniqueBodyWrapper.appendChild(uniqueBodyDOM);
return uniqueBodyWrapper;
},
/*
Generates the footer for the page
lastModifiedDate: The date of last modification of the page content
footerDataWrapperClasses: The classes to assign to the "author" and "date modified" wrappers.
returns: The generated footer
*/
generateFooter: function(lastModifiedDate, footerClasses, footerDataWrapperClasses) {
var footer = document.createElement('footer');
var authorWrapper = document.createElement('span');
authorWrapper.className = footerDataWrapperClasses;
var authorLabel = document.createElement('span');
authorLabel.innerHTML = 'Author:';
var authorName = document.createElement('span');
authorName.innerHTML = 'Brendon Williams';
this.appendChildren(authorWrapper,
[authorLabel, authorName]);
var lastModifiedWrapper = document.createElement('span');
lastModifiedWrapper.className = footerDataWrapperClasses;
var lastModifiedLabel = document.createElement('span');
lastModifiedLabel.innerHTML = 'Last Modified:';
var lastModified = document.createElement('span');
lastModified.innerHTML = lastModifiedDate;
this.appendChildren(lastModifiedWrapper,
[lastModifiedLabel, lastModified]);
this.appendChildren(footer,
[authorWrapper, lastModifiedWrapper]);
return footer;
},
/*
Helper function to append the list of children to the parent.
*/
appendChildren: function(parent, elements) {
for (var i = 0; i < elements.length; i++) {
parent.appendChild(elements[i]);
}
},
createCSSLink: function(cssPath) {
var cssLink = document.createElement('link');
cssLink.rel = 'stylesheet';
cssLink.type = 'text/css';
cssLink.href = cssPath;
return cssLink;
}
};
/*
A list-item object to assist in passing links.
*/
var LI = function(linkText, linkAddress) {
this.linkText = linkText;
this.linkAddress = linkAddress;
};