CSS, HTML, JavaScript, Tools & Generators

ATOM Packages

A lot of good text editors, like Sublime Text and Atom, have the ability to easily install packages that will extend their core functionality.

Sometimes, people will make packages to replicate functionality found in other editors. For example, I am a longtime Sublime Text user. However, since Atom is free, I’ve started using it so students don’t feel compelled to buy Sublime Text. But there are a few features of Sublime that I miss in Atom. Fortunately, a number of packages allow me to add those features.

To install a package in Atom, do the following:

  • Bring up the Command Palette (command-shift-p on Mac, control-shift-p on Windows or Linux)
  • Type install and then select Install Packages And Themes
  • Search for the package you want to install
  • When you find one, press the INSTALL button

Here are some of the packages I always install:

  • Emmet: a brilliant, and essential, HTML/CSS macro utility.
    And here’s the official Emmet documentation and the excellent cheatsheet.
    Emmet practice exercise 1 | answer key | Emmet practice exercise 2
  • Open in Browser / Open in Browsers. There are number of packages that make testing the page in the browser quicker.
  • Col0r-Picker: command-shift-c (mac) or control-alt-c (win/linux) brings up a color picker
  • Atom-Live-Server: for testing dynamically generated pages
  • Atom-Beautify: for tidying up html, css, javascript, php, etc.
  • Auto-Update-Packages: does what you think it would do
  • Wrap in Tag: this replicates Sublime Text’s wrap in tag command. Which is really useful.
  • Linter: linters provide error messages. ( You will need to install language-specific linters in addition to this “base” linter )
  • Linkter-htmlhint: this provides HTML error messages on save (look at the bottom of the editor. Click for error panel.)
  • Linter-CSSLint: this provides CSS error messages on save (look at the bottom of the editor. Click for error panel.)
  • Linter-jshint: for JavaScript hinting on save (look at the bottom of the editor. Click for error panel.)
  • Pigments: A CSS color viewer. It adds a background color to all your color declarations. It even understands SASS variables and color-changing functions.

That’s just a few of the available useful packages.

Note, also, that most packages also have settings that can be configured from the Preferences / Settings menu.

 

Standard
CSS, HTML

Float-based layout exercise: Bolt 1

For this exercise, please download the starter files. As well, please download screenshot package 1 and screenshot package 2.

In the starter files package you downloaded, you will find an index.html file, and a folder of images.

The index.html file has a header, a simple menu, and a number of stories. The stories are grouped by topic into sections.

There are a number of SECTIONS. Each section contains a heading and three stories.

Your task here is to produce the three responsive layouts and typography shown in the screenshots: phone, medium, and large, as well as that in the hover screenshot,

You are free to edit the HTML file if needed.

In the large layout, the main content area never gets wider than 1200px. It is centered in the window.

The font for the story titles is the Google font CUTIVE.  All other text is in GEORGIA.

I will leave you to figure out the colors used. Please get as close as possible.

You can use any resource: notes, internet, etc.

If you talk or look at someone else’s work, you will be asked to leave and will receive a zero mark for the exam.

One final suggestion: from time to time, make a backup of your project, in case you try something that doesn’t work and you can’t quickly figure out how to recover from the problem. The easiest way to do this on a Mac is to option-drag your folder. Or you can right-click and choose DUPLICATE.

You have three hours.

When you are done, hand in your exam to your “hand in” folder on studentshare.

Standard
CSS, HTML

Float-Based Layout Practice Exercises

If you want to practice float-based layout techniques, here are a few exercises. There are some very useful techniques contained in these exercises.

If we have not yet covered a technique or tag yet in class, google it.

Berlin Photo Gallery

Go to this page and download the files and follow the setup instructions. This exercise has a lot of hints in the instructions. It also has a highly annotated (HMTL & CSS) solutions file to download, if you want.

Upside Down Mag

Go to this page and download the files and follow the setup instructions. This exercise has a lot of hints in the instructions. It also has a highly annotated (HMTL & CSS) solutions file to download, if you want.

 

The other exercises, below, are arguably harder, and they have much less hints. Try them if you like a challenge.

Gibson Vs Fender 1

Download these files. Read the included Instructions  file. Build the various responsive layouts shown in the included screenshots.

Techniques Required

  • Floats
  • Clears
  • Clearfix
  • “CSS Math”
  • Border-radius
  • Position (for the heading / border trick… )

Gibson Vs Fender 2

Download these files. This package does not include an instructions file. Just build the various responsive layouts shown in the included screenshots.

Techniques Required

  • Floats
  • Clears
  • Clearfix
  • “CSS Math”
  • Nested Elements

PedalMania

Download these files. Read the included Instructions file. Build the various responsive layouts shown in the included screenshots.

Techniques Required

  • Floats
  • Clears
  • Clearfix
  • “CSS Math”
  • Background Images
  • Background Color
  • Tables
  • Hover
  • Position (for the heading – border trick… )
Standard
CSS

Using Google Fonts

Using a hosted font service like Google Fonts or Typekit allows us to use a much wider range of fonts and weights than are included in the default range of fonts resident on users’ computers (as itemized at cssfontstack.com).

Because the fonts will be downloaded to the user’s computer when the page loads for the first time, it is important that we  specify only  the fonts and weights we will actually use in our designs. If we want to try out a bunch, that’s completely OK, but we need to remember to edit the list of downloaded fonts or weights before we put the site goes live.

Note also that we can use a mixture of hosted and non-hosted fonts.

Anyway, here I am going to select a few weights of two fonts (as used in the guitarmania 2017 exercise elsewhere on this site).

First, I go to https://fonts.google.com:

Using the filtering options, you can search for fonts by name, or view them by type, thickness, etc.

To select, click on the + sign.

So first search for and select the OSWALD font, then search for and select ROBOTO SLAB.

In the screenshot below, I am looking at Roboto Slab, and I’ve changed the text sample to text reflective of how I’m going to use it in the exercise. Testing weights helps me determine that I will likely use the 400 weight with this face.

At the bottom of the screenshot, there is a black tab listing how many families are currently selected:

Click on that tab.

Here we see our two chosen fonts. If you decide to remove one, you can do so by clicking the minus sign near the name.

As noted, we’ve chosen a single weight of Roboto Slab. But I’m going to choose a few weights of Oswald so that we can test them in our actual project. To do that, we click the CUSTOMIZE link in the “Families Selected” tab box.

Add the four weights below.

The interface now downgrades our download time from FAST to MODERATE. But we’ll decrease our download time shortly.

Add the Link to the Fonts to Your Project

Now click back on EMBED.

The pane will now show the HTML & CSS for using the fonts.

However, if we use the HTML way of getting the fonts, we will need to add that link to all of HTML pages. An easier way is to click on @IMPORT and then copy part of the declaration (the selected text below) and paste it at the top of your stylesheet file:

Now I will test which weights of Oswald I want to use in my project:

h1 {
  font-family: Oswald, "arial narrow", sans-serif;
  font-size: 2rem;
  font-weight: 700;
}

h2 {
  font-family: Oswald, "arial narrow", sans-serif;
  font-size: 1.5rem;
  font-weight: 400;
}

If, though this testing, I decide that I need the 400 and 700 weights only, I can now edit the line (copied from Google Fonts) at the top of my stylesheet. Originally, we had this:

@import url('https://fonts.googleapis.com/css?family=Oswald:300,400,500,700|Roboto+Slab');

But here I have removed two unused weights

@import url('https://fonts.googleapis.com/css?family=Oswald:400,700|Roboto+Slab');

It is always a good practice to reduce as much file size as possible on all resources your page downloads. A commonly cited metric for Walmart is that for every 1 second decrease of page download time, the Walmart site increased conversions by 2%. That’s a lot of money.

Incidentally, the Google guide to getting started with Google Fonts has some very interesting information, including how to subset your chosen font. If you’re using a font for just a logo or a single heading, for example, subsetting can decrease the weight of the font by up to 90%.

Standard
CSS, HTML

Responsive Design Exercise: GuitarMania 2017

First, please download the files you’ll need for this exercise.

Once you unpack the downloaded zip file, open the main folder in your code editor. You will see that you have two folders: one for images and one for your CSS files.

In this exercise, we are going to use a mobile first methodology to build the index page of a site devoted to Gibson guitars.

If you are taking my ECUAD Advanced WordPress course, we will build this site and then turn it into a WordPress theme.

Here’s what our final comp is going to look like:

Desktop View: Click for expanded image

 

And here it is in the iPhone simulator:

iPhone view. Click for expanded image.

 

The keys to this kind of layout:

  1. Percentage-Based Widths
  2. Fluid Images
  3. CSS3 Media Queries
  4. The Viewport Meta-Tag

The Basic Setup

First, we will download a reset stylesheet to use as a foundation for the project.

To do this, go to the normalize.css webpage and download the latest version of this “starter” stylesheet into the empty css folder. Name it normalize.css. 

This stylesheet neutralizes differences between browsers so we can build upon a level playing field. One browser might have different default values for margin, or padding, or font-sizes, etc etc,  than another browser. This can cause layout problems. The purpose of reset stylesheets is to get all browsers reset in such a way that they behave the same way with all elements.

 

Next, go to HTMLShell.com to get a starter HTML file. Before copying it, however, click the button to include the viewport meta-tag. I will explain what viewport does later in this exercise. Save the index.html file at the top level of the site folder.

Wire up the stylesheets to this html file by adding the following two lines inside the <head> of your document.

<link href= "css/normalize.css" rel= "stylesheet">
<link href= "css/style.css" rel = "stylesheet">

Make a file called style.css and save it inside the css folder.

Put the phrase Guitar Mania inside your <title> tags.

Inside the <body> of your document, put in the following code for the header .

( Type it out rather than cutting and pasting from my website: this will help you memorize the tags and concepts. )


<header>

<h1>Guitar Mania</h1>

<nav>
 <ul>
  <li><a href="#">Guitars</a></li>
  <li><a href="#">Amps</a></li>
  <li><a href="#">Players</a></li>
  <li><a href="#">Lessons</a></li>
  <li><a href="#">Search</a></li>
  <li><a href="#">Contact</a></li>
 </ul>
</nav>

</header>

Test your page. Make sure that this content is showing up.

Add The Main Content

Now wrap everything that’s inside the body tags inside a DIV with a class of wrapper. This will set the ultimate wrapper for our content. We will style it shortly.

After the closing <header> tag (but inside the .wrapper DIV), put in the following code.


<section class="original">

  <h2>Original Models</h2>

  <div class="guitar-small">
    <a href="#">
      <img src="images/custom.jpg">
      <h3>Les Paul Custom</h3>
    </a>
  </div>

  <div class="guitar-small">
    <a href="#">
      <img src="images/goldtop.jpg">
      <h3>Les Paul Goldtop</h3>
    </a>
  </div>

  <div class="guitar-small">
    <a href="#">
     <img src="images/standard.jpg">
     <h3>Les Paul Standard</h3>
    </a>
  </div>

</section>
<!-- END ORIGINAL -->

What we are doing here is the following:

  • Grouping the content with a section tag and an explanatory h2.
  • For styling purposes, wrapping a DIV around each image and the h3 text that identifies it.
  • Wrapping a link around the image and the h3 (even though the anchor tag is inline, this is allowed).

Test the page in a browser. It should now look like this:

 

The first problem we notice is that under a certain size, the images go beyond the edge of the browser window rather scaling with the page.

Responsive Images

The solution, and a fundamental technique of Responsive Web Design, is to make your images resize to fit your box. It’s easy to do this.

Open the file style.css if it isn’t already open. Add the following style:

img {
  max-width: 100%;
  height: auto;
}

.guitar-small img {
  border: 1px solid #888; /*For design, not for responsiveness. */
}

Test your page now. Shrink it to see if the images are scaling down and have the border. The page should look like this:

Images scaling responsively

Make More Rows

If it does look like this, copy that row of guitars (the <section> with a class of original). If it doesn’t look like this, consider validating your html & css.

Paste that row twice after it. Change the classes of the pasted outer <section> elements to budget and artists, respectively.

Change the h2 text in the second row to Budget Models and the H2 text in the third row to Artist Models.

In the second row, change the H3s to: Les Paul Junior, then another Les Paul Junior, and finally Les Paul Special.

In the third row, change the H3s to: Alex Lifeson, then Peter Framptonand finally Ace Frehley.

With rows two and three, change the images in the inner sections to their correspondingly named guitar pictures (there are two Les Paul JRs, but the image names reflect that).

Finally, change each comment that ends each row, to reflect the changes you’ve made to each section.

Test the page. Make sure that all the images and headings are different.

Typography

Go to the Google fonts website and select the fonts Oswald (weights 400 & 700) and Roboto Slab (weight 400 only). If you need to review how to do that, consult this page.

Now let’s make our basic typographic decisions. In your style.css file, add the following CSS;

h1, h2 {
	font-family: oswald, sans-serif;
}

h1 {
	font-weight: 400;
	color: white;
	background-color: #000;
	margin-top: 0;
	padding-top: 3rem;
	text-align: center;
}

h2 {
        font-weight: 700;
	text-transform: uppercase;
	font-size: 2.6rem;
	text-align: center;
	margin-bottom: .75rem;
	margin-top: 2rem;
	line-height: 1;
}

h3 {
	font-family: "Roboto Slab", georgia, serif;
	font-weight: 400;
	margin: 0;
	font-size: .75rem;
}

a {
	font-family: "Roboto Slab", serif;
}

Test your HTML page at a narrow browser width. It should look more or less like this:

Obviously, our content is getting too close to the end of the browser window.

Wrapper DIV

The purpose of the WRAPPER div we put in earlier was to have a box with which to easily style our main layout.

Add the following to your stylesheet. Because the wrapper div is our main structuring element, I would put it just after the BODY style.

.wrapper {
  margin: 0 2%;
}

The page should now look like this:

In other words, we have now pushed the all the content away from the left and right edges of the browser.

Later, when we work on our desktop view, we will modify the .wrapper style more.

More Header Tweaks

Let’s work on the menu.

/* MENU and LINKS =========================================== */
a {
  font-family: "Roboto Slab", serif;
  text-decoration: none;
}

nav ul {
  list-style-type: none; 
  margin-left: 0; 
  padding-left: 0;
}

nav li {}

nav a {}

The NAV UL style here is an extremely common approach: we are removing the LIST dots with list-style-type: none and clearing up the space the browser made for the dots. ( We use margin-left and padding-left because different browsers have used different defaults ).

If you test the page, you will see that the dots are gone and the list is now sitting fully to the left of the wrapper.

Now let’s arrange and style the menu as in the iPhone simulator screenshot at the top of this page.

Modify your link and menu styles as follows:

nav a {
  font-size: 1.4rem;
  line-height: 2;
}
nav li {
  background-color: #ccc;
}

This will create the following presentation of our menu:

Now add the following styling

nav li {
  background-color: #ccc;
  border: 1px solid white;
  float: left;
  width: 50%;
}

Test the page. The results might surprise you:

Two things are happening here:

  • Because our LIs have borders, their width is 50% + 2px. This means that they won’t go beside each other.
  • The h2 is coming up beside the floated LI elements.

This kind of thing often confuses people, but there is an easy fix. Add box-sizing:border-box to your NAV LI style. This will make the browser treat the border (and padding, if the style has it) as inside the box. Very useful, indeed.

A few more tweaks remain. First, add the following to your h1 style:

h1 {
  border-bottom-right-radius: 1rem;
  border-bottom-left-radius: 1rem;
}

This will give you rounded bottom corners on your site name (the h1).

Add text-align:center to your NAV LI style to get the links to sit in the center of their containing elements.

Finally, add display: block to your NAV A style. This will make the links cover the full width of their containing elements (so mousing over the LI will cause the browser to display the pointer cursor that indicates links).

Test your page. We’ve got much of the mobile layout covered now.

Our First Clearfix

But if you scroll down the page, you will notice that the browser appears to be ignoring the margin-top on the first h2.

In contrast to the above screenshot, here is the second h2 in the page.

Both h2s share common styling.

What’s happening here, however, is common when you use floated elements. Specifically, if all the children (or the last children) of an element are floated, the parent element will appear to shrink (ie backgrounds will disappear and borders will collapse). The reason for this is to allow subsequent elements to come up beside the floated element.

This is, in fact, desired behavior: think of a tall image in a short paragraph: if we float it, we want subsequent paragraphs to come up beside the image.

In our case, since the NAV UL’s child LIs are all floated, they hang out of the UL.

The secret to fixing that is to use the clearfix hack.

For an explanation of clearfix, read this.

Copy the clearfix code from here. (You can omit the comments and the IE 6/7 code, too). Paste it into your style.css file.

NoW, apply the clearfix class as an attribute to the UL that’s inside your NAV.

<ul class="cf">

Test your page. You should see that the clearfix has made it appear that the first h2 now has a top margin again. This is because the UL has “grown” to encompass its floated children.

Additional Layouts: Media Queries

Media Queries allow us to direct additional or different styles to different viewing conditions.

For example, we could hide advertisements and change the paragraph font-size when the document is printed (notice the used of points and inches, which make sense for print but not for screen):

@print {
  .ad {
     display: none;
  }

  p {
    font-size: 9pt;
    line-height: 11pt;
    margin: .25in;
  }

}  /* end print query */

Each query starts with an @ symbol. Often it will also include a condition. For example, this series of queries will change the body background-color as the page size increases:


@media screen and (min-width: 500px) {
  body{
     background-color: orange;
  }
} /* end 500px min query*/

@media screen and (min-width: 800px) {
  body{
     background-color: red;
  }
} /* end 800px min query */


@media screen and (min-width: 1000px) {
  body{
     background-color: yellow;
  }
} /* end 1000px min query */

Think of each query as an additional stylesheet.

As noted, we’ve been building this guitar site using the Mobile First methodology. The simple background-color example above shows how we will continue the build: with a series of escalating-value min-width media queries.

Here’s why Mobile First makes so much sense:

Since block elements already take up 100% of their containers, and since typical phone layouts are often predominantly single-column, the phone layout is the easiest one to do.

Moreover, by first making the styles that are common to all our layouts, we can build UP rather than DOWN. In other words, we will add more complex layout styles as the browser width increases, rather than remove them as the browser width decreases.

When smart phones first came out, designers typically took a Mobile Last approach. They would build the wider views and then use media queries to simplify or undo some of the layout at smaller widths. Mobile First means less work because we don’t have to undo anything we build.

Back to our Task

If we look at the desktop width screenshot at the top of the page, we’ll see some obvious divisions. For example, the h2s and the guitar-small DIVs are four per row. So we could style them like this (put the query after your other styles, so they don’t get overridden):

@media screen and (min-width: 700px) {
  h2, .guitar-small {	
	margin: 1%;
	width: 23%;
	float: left;
  }
} /* end 700px min query */

Test your page. Shrink it as much as the browser will allow. Here we see the phone layout. Now make the page wider. Eventually we will see the 700+ layout change to something like this:

As you make the page bigger, the layout will end up looking like this:


The reason for the first problem (700px to approximately 800px) is that as the images scale down, their enclosing DIVs get shorter, too. As a result, the h2 boxes are taller than the .guitar-small boxes. This causes each succeeding row to get enjambed.

The reason for the second problem is that the Les Paul Special image is a pixel or two shorter than the other ones. As a result, the h2 in the third row gets enjambed, too.

The solution to both problems: clearfix the SECTIONS. This will make each section exactly as tall as the highest element. As a result, the bottom of each section will be smooth rather than uneven, which means that succeeding content will not get enjambed.

(To add another class to an element that already has one, put it inside the ” ” and separate the classes with spaces).

 <section class="artist-models cf">

Once we clearfix the three sections, our layout should look like this.

Your Remaining Tasks: Add and Style More Content

Please download the final layout screenshots.

Open them up in Preview or some other PDF reader and view them at actual size.

Note the additional content in the screenshot. Please add it in. The image is of a Gibson ES-175, which you can find in the images folder.  The h3 that goes with the image says Featured and the paragraph below that says:

It’s not a Les Paul, and we’re a Les Paul site, but this Gibson ES-175 is so gorgeous that we decided to feature it anyway.

Beside that (in the Desktop view) is a form entitled Contact The Editors, with fields for First Name, Last Name, Email, and Comments.

I’ll give you the code for the form:

<section class="subscribe">

  <h4>Contact The Editors</h4>

  <form action="#" method="post"> 

    <label for="firstname">First Name</label>
    <input type = "text" class="firstname" id="firstname">

    <label for="firstname">Last Name</label>
    <input type = "text" class="lastname" id="lastname">	

     <label for="email">Email</label>
     <input type="email" class="email" id="email">

     <label for="comments">Comments</label>
     <textarea id="comments" class="comments"></textarea>

     <input type="submit">

  </form>
</section>

You figure out how to style the form. Hint: the display property is very useful here.

Please finish the page layout, getting it as close as possible to those shown in the downloaded screenshots, and then show your page to me.

About the Viewport Meta Tag

The Viewport Meta Tag, which we added when we got the HTML from HTMLShell.com, tells mobile browsers not to scale the page down.

First making sure that the meta tag is in your code, test your page in the Apple mobile simulator. Or test it on your phone by uploading it to webspace and then going to that URL on your phone.

Comment out the viewport meta tag line and then test it again in the mobile simulator or on an actual phone.

Doing this will show how mobile browsers scale pages without viewport meta tags, assuming that they were built without a responsive strategy (as practically all pages were built before the advent of smart phones).

In essence, when you use the viewport meta tag, you tell the mobile browser that you have in fact taken its dimensions into account in your design.

From the Mozilla Developers Network, more information on why the viewport meta tag is required.

Guitar images © Gibson Brands. Used with permission.

Standard
CSS, HTML

Bolt Book Club Exercise Explanation

To do the math for the layout for the exercise, there are two approaches you could use: with or without the use of box-sizing: border-box.

Depending on which you choose, the measurements would be as follows:

.book {
	margin: 1rem 1.75%;
	padding: 1.25%;
	box-sizing: border-box; /* optional */
}

@media screen and (min-width: 450px) {
	.book {
		float: left;
		width: 46.5%; /* if using border-box */
		width: 44%; /* if NOT using border-box */
	}
}

@media screen and (min-width: 1000px) {
	.book {
		width: 21.5%; /* if using border-box */
		width: 19%; /* if NOT using border-box */
	}
}

The logic is this:

If we want two boxes to sit side by side and to take the full width of their container, they need to occupy 50% each. If we want four boxes to do that, they need to occupy 25% each.

If we use box-sizing:border-box, then the width of the box INCLUDES the padding. Therefore we just need to subtract the left and right margin from the space we want the box to occupy:

25% – 1.75% – 1.75% = 21.5%
50% – 1.75% – 1.75% = 46.5%

In other words, we need to subtract 3.5% from our desired occupied space.

If we don’t use box-sizing:border-box, then the width of the box DOES NOT INCLUDE the padding. So we need to subtract the padding-left and padding-right, too.

25% – 1.75% – 1.75% – 1.25% – 1.25% = 19%
50% – 1.75% – 1.75% – 1.25% – 1.25% = 44%

In other words, we need to subtract 6% from our desired occupied space.

Standard
CSS, HTML

FlexBox Photo Blog Layout Exercise

In this exercise, you will make a photo blog home page. Please download the starter files and the screenshots files.

There are three sections to this page: a header, a main area, and a footer.

The font used thoughout is the Google font Old Standard TT.

Mark Distribution

Header / Footer: 20%
Small, Medium, Large: 50%
Extra-Large: 30%

Bonus Task: 20%

Header / Footer

The header and footer look and behave almost the same way: the only difference is that the footer does not have the site name (light). In the header, the site name box will take 1/3, while the nav element will take 2/3. It will look like this at full width:

menu full size

As the page width decreases, the header area will arrange itself like this:

menu small

In the footer, make the menu take the full width of the page (the screenshot is showing a small slice of the MAIN area, so you get a better idea of how the footer behaves in relation to it).

You do not have to make the items into actual links. Just focus on getting the layout correct.

Main Area

The main area consists of 30 articles. Each article has an h2, an image in a DIV, and a single paragraph.

There are four responsive states: small, medium, large, and extra-large. Click on any of the screenshots for a bigger view, or consult the screenshot files you downloaded from the top of this page.

Small

small layout

Medium

medium layout

Large

Extra Large

Extra Large Layout

 

Bonus Task

For a 20% bonus, figure out how to add light grey background to alternating rows of articles, starting with the second row, without using classes or adding to the HTML.

Do not add the striping in single column view. Add it once the articles have rows of two, three, or four articles each.

Obviously, the striping must change with the responsive breakpoints.

row striping

 

All images in this exercise are from Flickr, with creative commons licensing. Attributions are here.

 

 

Standard
CSS, HTML, JavaScript

JavaScript Shakespeare Exercise Solution

Since posting the original exercise, I have made a few changes to the look of the page, so if you download the completed version, do not be surprised that some features are different from those described in the exercise.

First, download the slim minified version of jQuery and add a link to it just before the closing of the body tag. Then add a link to a new .js file after the jQuery link:

<script src="js/jquery-3.1.1.min.js"></script>
<script src="js/shakespeare.js"></script>

Planning the Script

Before coding anything we need to figure out what the page actually needs to do. Each component of the task can be coded as a function for invocation in the (document).ready() handler as well as in other functions.

Here are most of the planned functions:

  • Sliding instructions panel up when it is clicked
  • Sliding instructions panel down when button is clicked
  • Hiding quotes and titles
  • Selecting and displaying a random quote on click of genre panel
  • Displaying the titles in the clicked genre panel
  • Styling the active panel and titles
  • Comparing clicked title to title of random quote
  • Updating score
  • Generating random insult
  • Displaying random insult

The Instructions Panel

As noted above, the instructions panel will slide up when it is clicked, and slide down when the instructions button is clicked. When down (as at page load), it will take up the full width and height of the screen.

So first let’s style it.

/* INSTRUCTIONS SECTION ======================================= */
.instructions-text {
	color: white;
	background-color: rgba(0,0,0,.85);

	font-size: 1.5rem;
	position: absolute;
	top: 0;
	left: 0;
	right: 0;
	bottom: 0;

	display: flex;
	flex-flow: column nowrap;
	justify-content: center;
	align-items: center;
}

.instructions-text:hover {
	cursor: pointer;
}

.instructions-text ul {
	display: flex;
	flex-flow: column nowrap;
}


By using four values on the positioned instructions, we can get it to cover the entire width and height of the browser window. The flex values are to center the text horizontally and vertically. By setting the cursor to pointer, we provide a hint that the panel is clickable (when the user wants to hide the instructions).

Now lets add the jQuery, beginning with the $(document).ready() handler: the function you will start most jQuery scripts with. I like to keep that handler clean by making functions of separate tasks and then calling them from the ready() handler, but there are other ways to do it.

$(document).ready(function(){	
	showHideInstructions();
});

function showHideInstructions(){
	$('.instructions-text, header button').on('click', function(){
		$('.instructions-text').toggleClass('instructions-toggled');
	});
}

The “on” method is the jQuery way of adding an EventListener (if you’re familiar with “vanilla” js). The first element passed to that method is the event to listen for (passed as a string), then the function that happens when that event is triggered.

Here we have added an EventListener to each of two items (the instructions panel and the button in the header). The toggleClass method does what you might expect: adds a class if the targeted element does not have it, and removes it if it does.

Obviously, though, we need here to add the instructions-toggled class to our stylesheet.

.instructions-toggled {
	transform: translateY(-100%);
}

This transformation will move the panel up the Y-axis 100% of its height, hiding it off screen.

Test it in the browser.

You will see that the menu disappears when clicked, and reappears when the button in the header is clicked. However, we need to make a transition so the change is not instaneous. Although jQuery has transition methods, it is typically more processor-friendly to apply transitions via CSS. So add the following to your .instructions-text style: transition: transform .5s;

Test again: the panel slides up and down when the panel or the button are clicked.

Hiding Titles and Quotations

If you examine the HTML I gave you, you will see that I have put the play titles and associated quotes in a Description List (DL), with the titles marked up as DT elements and the quotes as DD elements. Hiding them is again as easy as selecting them and toggling a class:

function hideAll(){
	// hide all quotations and titles
	$('dd, dt').addClass('hidden');

	// clear out quotation box
	$('.quotation').text('');
}

If you know JavaScript, you will no doubt note here that you do not need to loop through the returned node list to toggle the classses (if you don’t know JS, that comment won’t make sense, of course).

If you further examine the markup, you will see that I have put an empty quotation DIV in each panel, between the heading and the DL. Later when we generate the random quote, we will put the quote into that DIV, but in this hideAll() function, we will insert an empty string.

The reason we will do this is so that the box empties of text every time the function is invoked. That way, whenever we generate a new random quote for example, it won’t be added to the previous quote).

Now add this function to the (document).ready() handler:

$(document).ready(function(){	
	showHideInstructions();
        hideAll()
});

Test your page in the browser. The main panels should now only show the genres (HISTORIES, TRAGEDIES, COMEDIES, PROBLEM PLAYS).

Showing Titles and Random Quotation in One Panel

When a user clicks on a genre panel, it needs to “open” and show the titles and a random quote. As well, any open panels must close.

Each genre panel has a class of menu-section. We will attach an EventListener to each via the jQuery on method.

When a panel is clicked, the handler will first of all invoke the hideAll() function we wrote earlier. This will close the panels, which means that when we open the clicked one, we won’t have two open.

Then we save in a variable a reference to the panel that was clicked: the $(this) construction saves a reference to the target of the click event. By wrapping the JavaScript THIS in $(), we convert it into a jQuery object, which means that we can use a range of jQuery methods on it.

For example, in the next line, we add a class to the clicked panel, so that we can use styles that apply to the currently-selected panel. As well, with the $(this) object (saved here as genre), we can search within the active panel (using the <b>find</b> method) for DTs, which hold the play titles.

Having returned a jQuery list of DTs in this clicked panel, we can then toggle the HIDDEN class on these DTs only. This means, in short, that the DTs in the other panels will remain hidden, while those in the clicked panel will now be visible.

function onGenreClick(){
	$('.menu-section').on('click', function(){
		
		hideAll();

		var genre = $(this);

		// FOR STYLING PURPOSES, we can then a use descendent selector 
		// to style the titles.
		genre.addClass('active');
		
		// Make TITLES visible in this panel only
		genre.find('dt').toggleClass('hidden');

                // Check how many titles there are
		var titleNum = genre.find('dt').length;

                genre.find('.quotation').text("sample text");
		// genre.find('.quotation').text(randomQuote(genre, titleNum));

	});
}

Add the onGenreClick() function to the document ready handler.

Test the file.

Everything should work. If something does not work, go to the Console in the Inspector and see if you can work out what the error message is telling you.

Obviously, we will need to set our  hidden class to display: none.

As well, if you look in the QUOTATION div, you will see that we have just written the words sample text into the quotation box.  (Using the text() method, incidentally, we can set text and we can get text.)

Delete the line that is writing the text into the quotation DIV, and uncomment the line after it.

Now when you test the file, you will definitely get an error. The reason is in the last part of this function. Instead of writing sample text we will write a random quote by passing a function called randomQuote to the text() method.

The problem, however, is that we have not yet written that randomQuote function.

Generating the Random Quote

If you look at the reference to the randomQuote function in the code above, you will see that inside the brackets there are two variables being passed to the function: genre and titleNum.

The first, genre, is the saved reference to the clicked panel itself. The second is the number of DTs inside that panel, which we determined using the length property.

So here is the actual function:

function randomQuote(genre, titleNum){

	var randomNumber = Math.floor(Math.random() * titleNum);
	var theQuote = genre.find('dd').eq(randomNumber).text();
	
	return(theQuote);
}

In the first line inside the function, we generate a random number between 0 and .999999 (etc) which we then multiply by the number of titles passed in via the titleNum variable. Then we use Math.floor() to round the number down.

The result is that our random number will be between 0 and (the number of titles minus 1). This is useful, because the jQuery set of returned elements is indexed like an array (starting at 0).

The logic of the line before the return function is this

  • within this panel (genre), find all the DD elements
  • with this list of DD elements, chose the one whose index is the same as the random number. ( EQ(number) will return the element at the specified index (location) in a set.)
  • get the text from inside the randomly selected DD

By passing the theQuote to the return function, we pass the quote back to where the randomQuote was called. That was in the previous function.

Note here, also, that we do not invoke this randomQuote function inside the $(document).ready() handler. The reason is that this function does not need to run immediately on page load. Rather, it is invoked by another function when needed.

Making the Titles Clickable

We now need to make the titles (which reside inside DTs) clickable.

To do that we will add the following code to our script:

function onTitleClick(){
	$('dt').on('click', function(evt){

	// Prevent CLICK event from BUBBLING UP and triggering the section LINK
	evt.stopPropagation();
	
	var title = $(this);

	// Find the text of the next nearest sibling of the title DT.
	var associatedQuote = title.next().text();

	// Find what is in the QUOTATIONS box right now.
	var visibleQuote = title.parent().parent().find('.quotation').text();

	if ( associatedQuote == visibleQuote ){
		shakespeareScore++;
		shakespeareAttempts++;
	}
	else {
		shakespeareAttempts++;
	}
	
	updateScore();

	});

}

Add a reference to this function to the document ready function. It should now look like this:

$(document).ready(function(){
	
	showHideInstructions();
	hideAll();
	onGenreClick();
	onTitleClick();
});

Test the page. A number of errors will be flagged in the console.

Obviously, we have not yet written the updateScore function, so just comment it out for now.

As well, when we test whether the quotation has been identified correctly, we increment one or both of two variables: shakespeareScore and shakespeareAttempts. Those variables do not yet exist. To remedy that, add the following code above the document ready handler:

var shakespeareScore = shakespeareAttempts = 0;

Now the script should work.

The most important part of the above script is the evt.stopPropagation() code. What is happening here is that we are passing the event object (you can call it anything you like, like any variable, but it is usually called e or evt) and using stopPropagation with it.

This solves a frustrating problem: the DTs are clickable, but they are inside another element (the genre panel) which is also clickable. The problem is that a click event “bubbles up”: the click will trigger the event handler attached to the DT, but also the event handler attached to the .menu-section (genre panel). This means that clicking a title will trigger a new random quote.

stopPropagation, in other words, will prevent a click on a child element from also applying to any of the child’s ancestors.

Because of the structure of our HTML (DT + DD), the use of the next() method (along with the text() method, of course) will return the quote associated with the clicked title.

In a similar way, we will travel up the DOM from the clicked DT to the parent DL then the parent .instructions-text DIV. This will allow us to search the text of the QUOTATIONS DIV in order to compare title choice to actual title. ( We could have just stored that information in a variable, of course, which would be more efficent…)

Then we test to see if our two strings (guess title text vs actual title text) match. If so, the user gains a point.

Which means that it is time to write the updateScore() function.

Updating the Score

This is easy:

function updateScore(){
	$('.score span').text(shakespeareScore + '/' + shakespeareAttempts);
}

This function does not need to be added to the document ready handler, as it is invoked by another function (and we do not need it to run immediately when the page is fully loaded).

Test the page. Hopefully everything is working as expected.

The Insult Generator

To make the insult, we will write a function that when triggered does the following things:

  • takes a random adjective from the adjectives array
  • takes a second random adjective from the adjectives array
  • takes a random noun from the nouns array
  • joins these together in a sentence
  • displays that sentence on the page

Since we will need to generate random numbers multiple times, we will make a function that does this when invoked and passed the array length.

The generation itself is pretty easy: since items in an array can be accessed by number enclosed in square brackets, if we call our randomize function, passing it the length of the array itself, we will get a random word.


function generateInsult() {

var insult = "";

var adjectives = ["artless","bawdy","beslubbering","bootless","brutish","churlish","cockered","clouted","craven","currish","dankish","dissembling","droning","errant","fawning","fobbing","froward","frothy","gleeking","goatish","gorbellied","impertinent","infectious","jarring","loggerheaded","lumpish","mammering","mangled","mewling","paunchy","pribbling","puking","puny","quailing","rank","reeky","roguish","ruttish","saucy","spleeny","spongy","surly","tottering","unmuzzled","vain","venomed","villainous","warped","wayward","weedy","yeasty","base-court","bat-fowling","beef-witted","beetle-headed","boil-brained","clapper-clawe","clay-brained","common-kissing","crook-pated","dismal-dreaming","dizzy-eyed","doghearted","dread-bolted","earth-vexing","elf-skinned","fat-kidneyed","fen-sucked","flap-mouthed","fly-bitten","folly-fallen","fool-born","full-gorged","guts-griping","half-faced","hasty-witted","hedge-born","hell-hated","idle-headed","ill-breeding","ill-nurtured","knotty-pated","milk-livered","motley-minded","onion-eyed","plume-plucked","pottle-deep","pox-marked","reeling-ripe","rough-hewn","rude-growing","rump-fed","shard-borne","sheep-biting"];

var nouns = ["apple-john", "baggage", "barnacle", "bladder", "boar-pig", "bugbear", "bum-bailey", "canker-blossom", "clack-dish", "clotpole", "coxcomb", "codpiece", "death-token", "dewberry", "flap-dragon", "flax-wench", "flirt-gill", "foot-licker", "fustilarian", "giglet", "gudgeon", "haggard", "harpy", "hedge-pig", "horn-beast", "hugger-mugger", "jolthead", "lewdster", "lout", "maggot-pie", "malt-worm", "mammet", "measle", "minnow", "miscreant", "moldwarp", "mumble-news", "nut-hook", "pigeon-egg", "pignut", "puttock", "pumpion", "ratsbane", "scut", "skainsmate"];
var firstAdjective = adjectives[randomizeMe(adjectives.length)];
var secondAdjective = adjectives[randomizeMe(adjectives.length)];
var noun = nouns[randomizeMe(nouns.length)];

console.log(firstAdjective, secondAdjective, noun);

}

function randomizeMe(arg){
var randomNumber = Math.floor(Math.random() * arg);
return(randomNumber);
}

Test the page and go to the console to see if you are getting the three words output.

Modify the Function For Grammar

Add the to our generateInsult function, replacing the console.log operation.


var article = "a";

insult = "Thou art " + article + " " + firstAdjective + " " + secondAdjective + " " + noun + ".";
console.log(insult);

The reason I have turned the grammatical article into a variable will be become apparent if you go to the console and reload the page a number of times. Eventually, you will generate a firstAdjective that begins with a vowel. At that point, you will need to change the value of article to <b>an</b>.

The following code will do that.

The first line gets the first character of the firstAdjective variable. The next line tests if it is vowel. If so, it changes it.

var firstLetter = firstAdjective.charAt(0);
if (firstLetter == "a" || firstLetter =="e" || firstLetter == "i" || firstLetter == "o" || firstLetter == "u" ) {
article = "an";
}

(Make sure that you add the code before the line that creates the full sentence, so the sentence uses the correct article.)

Reload the page a number of times and check that the correct article is ending up in the sentence.

Wire Up the InsultMe Button

If we look in the footer of the document, you will see a button with an ID of <b>insult-trigger</b>and a DIV with an ID of <b>insult</b>. Let’s wire up that button so that it outputs the insult when clicked.

First change the generateInsult function to add a return(insult) in the last line.

Then add the following.


function onInsultMeOnClick() {
$('#insult-trigger').on('click', function(){
$('#insult').text(generateInsult());
});
}

Finally, now add the onInsultMeOnClick() function to the document ready handler.

Test your page.

Hopefully everything is working.

Download the solution file. 

 

 

Standard