Skip to content
This repository has been archived by the owner on Jun 28, 2022. It is now read-only.

INITIAL COMMIT - WEB OPTIMIZATION PROJECT - AKIM #132

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
82 changes: 43 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,55 +1,59 @@
## Website Performance Optimization portfolio project

Your challenge, if you wish to accept it (and we sure hope you will), is to optimize this online portfolio for speed! In particular, optimize the critical rendering path and make this page render as quickly as possible by applying the techniques you've picked up in the [Critical Rendering Path course](https://www.udacity.com/course/ud884).
I was tasked with optimizing this online portfolio for speed! In particular, optimize the critical rendering path and make this page render as quickly as possible by applying the techniques I picked up in the [Critical Rendering Path course](https://www.udacity.com/course/ud884).



To get started, check out the repository and inspect the code.

### Getting started

#### Part 1: Optimize PageSpeed Insights score for index.html

Some useful tips to help you get started:
Website Location:

1. Check out the repository
1. To inspect the site on your phone, you can run a local server
PageSpeed Insights Scores:
1- Desktop:
2- Mobile:

```bash
$> cd /path/to/your-project-folder
$> python -m SimpleHTTPServer 8080
```
Changes Made to Attain these Scores:
Index.html
WITHIN THE HEAD TAG:
Added:
- meta tag for caching capability
- media attribute to link tage for print.css
- minified and inline critical css to reduce # of requests

Removed:
- link tag for style.css to reduce # of requests
- call to google api for font
- all non manipulating js - to unblock DOM construction

1. Open a browser and visit localhost:8080
1. Download and install [ngrok](https://ngrok.com/) to the top-level of your project directory to make your local server accessible remotely.
WITHIN THE BODY TAG:
Added:
- Compressed two(2) images [pizzeria.jpg and profilepic.jpg]

``` bash
$> cd /path/to/your-project-folder
$> ./ngrok http 8080
```
WITHIN THE HTML TAG:
Added:
- moved script tags from head tag; placed them after body tag therefore would not block DOM construction
- moved call to google api for font from head tag; used javascript to retrieve font; utilized the load function
- added async attribute to non manipulating js -

1. Copy the public URL ngrok gives you and try running it through PageSpeed Insights! Optional: [More on integrating ngrok, Grunt and PageSpeed.](http://www.jamescryer.com/2014/06/12/grunt-pagespeed-and-ngrok-locally-testing/)

Profile, optimize, measure... and then lather, rinse, and repeat. Good luck!

#### Part 2: Optimize Frames per Second in pizza.html

To optimize views/pizza.html, you will need to modify views/js/main.js until your frames per second rate is 60 fps or higher. You will find instructive comments in main.js.

You might find the FPS Counter/HUD Display useful in Chrome developer tools described here: [Chrome Dev Tools tips-and-tricks](https://developer.chrome.com/devtools/docs/tips-and-tricks).

### Optimization Tips and Tricks
* [Optimizing Performance](https://developers.google.com/web/fundamentals/performance/ "web performance")
* [Analyzing the Critical Rendering Path](https://developers.google.com/web/fundamentals/performance/critical-rendering-path/analyzing-crp.html "analyzing crp")
* [Optimizing the Critical Rendering Path](https://developers.google.com/web/fundamentals/performance/critical-rendering-path/optimizing-critical-rendering-path.html "optimize the crp!")
* [Avoiding Rendering Blocking CSS](https://developers.google.com/web/fundamentals/performance/critical-rendering-path/render-blocking-css.html "render blocking css")
* [Optimizing JavaScript](https://developers.google.com/web/fundamentals/performance/critical-rendering-path/adding-interactivity-with-javascript.html "javascript")
* [Measuring with Navigation Timing](https://developers.google.com/web/fundamentals/performance/critical-rendering-path/measure-crp.html "nav timing api"). We didn't cover the Navigation Timing API in the first two lessons but it's an incredibly useful tool for automated page profiling. I highly recommend reading.
* <a href="https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/eliminate-downloads.html">The fewer the downloads, the better</a>
* <a href="https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/optimize-encoding-and-transfer.html">Reduce the size of text</a>
* <a href="https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/image-optimization.html">Optimize images</a>
* <a href="https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching.html">HTTP caching</a>

### Customization with Bootstrap
The portfolio was built on Twitter's <a href="http://getbootstrap.com/">Bootstrap</a> framework. All custom styles are in `dist/css/portfolio.css` in the portfolio repo.

* <a href="http://getbootstrap.com/css/">Bootstrap's CSS Classes</a>
* <a href="http://getbootstrap.com/components/">Bootstrap's Components</a>
Website Location:

Changes Made:
pizza.html
- Compressed two(2) images [pizzeria.jpg and pizza.png]

main.js
- Update function changeSliderLabel() and determineDx ()
To use documnet.getElementById instead of document.querySelector because it's more efficient accessing the DOM
- Update changePizzaSizes() to help avoid forced synchronous layout bottle necking
To use documnet.getElementsByClassName & is more efficient accessing the DOM instead of document.querySelector
Moved reading layout properties outside of the loop and batch update the styling afterwards
- Update function updatePositions()
To use document.getElementsByClassName('mover') is more efficient accessing the DOM instead of instead of querySelectorAll(.'mover')
Moved all code reading layout property 'scrollTop' outside of the loop and batch update the styling afterwards
Reduced the number of pizza images appended to 25
Binary file modified img/profilepic.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/profilepic1.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
52 changes: 32 additions & 20 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,22 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content=" FILL ME IN ">
<meta name="author" content=" FILL ME IN ">
<title>Cameron Pittman: Portfolio</title>
<meta http-equiv="cache-control" content="Public" max-age="2592000"> <!-- cache -->

<!-- Hmm, what is the impact of web fonts on speed? Measure it... -->
<link href="//fonts.googleapis.com/css?family=Open+Sans:400,700" rel="stylesheet">

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

<script>
(function(w,g){w['GoogleAnalyticsObject']=g;
w[g]=w[g]||function(){(w[g].q=w[g].q||[]).push(arguments)};w[g].l=1*new Date();})(window,'ga');
<title>Akim George: Portfolio</title>

// Optional TODO: replace with your Google Analytics profile ID.
ga('create', 'UA-XXXX-Y');
ga('send', 'pageview');
</script>
<script src="http://www.google-analytics.com/analytics.js"></script>
<script async src="js/perfmatters.js"></script>
<!-- Hmm, what is the impact of web fonts on speed? Measure it... -->
<link href="css/print.css" rel="stylesheet" media="print"> <!-- reducing # of critical resources by adding media print-->
<!-- Minified and Inline CSS to prevent render blocking -->
<style>
.hero,footer{border-top:1px solid #ccc}footer span,li p{font-style:italic}html{font-size:100%;overflow-y:scroll;-webkit-tap-highlight-color:transparent;-ms-text-size-adjust:100%;-webkit-text-size-adjust:none}body{margin:0;font-size:14px;line-height:1.61;font-weight:400;background:#fff}b,header p,strong{font-weight:700}body,button,input,select,textarea{font-family:'Open Sans',sans-serif;color:#333}a{color:#12C}a:visited{color:#61C}a:focus{outline:dotted thin}a:active,a:hover{color:#c00;outline:0}code,pre{font-family:monospace,monospace;font-size:1em}ol,ul{margin:1em 0;padding:0 0 0 20px}img{border:0;max-width:100%}.container,footer,header{max-width:45em;margin:0 auto}header{padding:0 .5em;color:#C90B0B}header img{border-radius:40px;float:left}header p{font-size:1.5em;padding-left:4em}header p span{font-size:.8em;font-weight:400}.hero{padding:2em;background-color:#f8f8f8;font-size:1.2em;border-bottom:1px solid #ccc}.content{padding:1em}.content li{list-style-type:none;font-size:1.1em}li img{float:left;padding-right:1em}li p{font-size:.9em}footer{padding:0 .5em}footer span{float:right}@media only screen and (max-width:480px){body{font-size:12px}header p{padding-left:4.5em}}
</style>
</head>

<body>
<header>
<a href="/"><img src="img/profilepic.jpg"></a>
<p>Cameron Pittman<br><span>Course Developer</span></p>
<a href="/"><img src="img/profilepic.jpg"></a> <!-- compressed image file to save total bytes-->
<p>Akim George<br><span>FrontEnd Web Developer</span></p>
</header>

<div class="container">
Expand Down Expand Up @@ -57,8 +49,8 @@
</li>

<li>
<img style="width: 100px;" src="views/images/pizzeria.jpg">
<a href="views/pizza.html">Cam's Pizzeria</a>
<img style="width: 100px;" src="views/images/pizzeria.jpg"> <!-- compressed image file to save total bytes-->
<a href="views/pizza.html">Akim's Pizzeria</a>
<p>Who wants a performant pizza?</p>
</li>
</ul>
Expand All @@ -69,4 +61,24 @@
</footer>
</div>
</body>
<!-- moved script tags from head tag; placed them after body tag therefore would not block DOM construction -->
<!-- added async attribute to non manipulating js -->
<script src="https://ajax.googleapis.com/ajax/libs/webfont/1.5.18/webfont.js"></script></script>
<script>
WebFont.load({
google: {
families: ['Open Sans']
}
});
</script>
<script async>
(function(w,g){w['GoogleAnalyticsObject']=g;
w[g]=w[g]||function(){(w[g].q=w[g].q||[]).push(arguments)};w[g].l=1*new Date();})(window,'ga');

// Optional TODO: replace with your Google Analytics profile ID.
ga('create', 'UA-XXXX-Y');
ga('send', 'pageview');
</script>
<script async src="http://www.google-analytics.com/analytics.js"></script>
<script async src="js/perfmatters.js"></script>
</html>
Binary file modified views/images/pizza.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified views/images/pizzeria.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added views/images/pizzeria1.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
46 changes: 30 additions & 16 deletions views/js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -406,13 +406,13 @@ var resizePizzas = function(size) {
function changeSliderLabel(size) {
switch(size) {
case "1":
document.querySelector("#pizzaSize").innerHTML = "Small";
document.getElementById("pizzaSize").innerHTML = "Small"; // documnet.getElementById is more efficient accessing the DOM instead of document.querySelector
return;
case "2":
document.querySelector("#pizzaSize").innerHTML = "Medium";
document.getElementById("pizzaSize").innerHTML = "Medium"; // documnet.getElementById is more efficient accessing the DOM instead of document.querySelector
return;
case "3":
document.querySelector("#pizzaSize").innerHTML = "Large";
document.getElementById("pizzaSize").innerHTML = "Large"; // documnet.getElementById is more efficient accessing the DOM instead of document.querySelector
return;
default:
console.log("bug in changeSliderLabel");
Expand All @@ -424,7 +424,7 @@ var resizePizzas = function(size) {
// Returns the size difference to change a pizza element from one size to another. Called by changePizzaSlices(size).
function determineDx (elem, size) {
var oldWidth = elem.offsetWidth;
var windowWidth = document.querySelector("#randomPizzas").offsetWidth;
var windowWidth = document.getElementById("randomPizzas").offsetWidth; // documnet.getElementById is more efficient accessing the DOM instead of document.querySelector
var oldSize = oldWidth / windowWidth;

// Changes the slider value to a percent width
Expand All @@ -448,12 +448,17 @@ var resizePizzas = function(size) {
}

// Iterates through pizza elements on the page and changes their widths
function changePizzaSizes(size) {
for (var i = 0; i < document.querySelectorAll(".randomPizzaContainer").length; i++) {
var dx = determineDx(document.querySelectorAll(".randomPizzaContainer")[i], size);
var newwidth = (document.querySelectorAll(".randomPizzaContainer")[i].offsetWidth + dx) + 'px';
document.querySelectorAll(".randomPizzaContainer")[i].style.width = newwidth;
}
function changePizzaSizes(size) {
// documnet.getElementsByClassName & is more efficient accessing the DOM instead of document.querySelector
// Moved reading layout properties outside of the loop and batch update the styling afterwards
var randomPizzas = document.getElementsByClassName("randomPizzaContainer");
var dx = determineDx(randomPizzas[0], size);
var newwidth = (randomPizzas[0].offsetWidth);
var recalculatedWidth = (dx + newwidth) + 'px';

for (var i = 0; i < randomPizzas.length; i++) {
randomPizzas[i].style.width = recalculatedWidth;
}
}

changePizzaSizes(size);
Expand Down Expand Up @@ -501,11 +506,19 @@ function updatePositions() {
frame++;
window.performance.mark("mark_start_frame");

var items = document.querySelectorAll('.mover');
// document.getElementsByClassName('mover') is more efficient accessing the DOM instead of instead of querySelectorAll(.'mover')
// Moved all code reading layout property 'scrollTop' outside of the loop and batch update the styling afterwards
var items = document.getElementsByClassName('mover');
var scrollTop = document.documentElement.scrollTop / 1250 || document.body.scrollTop / 1250;
var phases = [];
var phase;
for (var i = 0; i < 5; i++) {
phase = Math.sin((scrollTop) + (i % 5));
phases.push(phase);
}

for (var i = 0; i < items.length; i++) {
// document.body.scrollTop is no longer supported in Chrome.
var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
var phase = Math.sin((scrollTop / 1250) + (i % 5));
phase = phases[i % 5];
items[i].style.left = items[i].basicLeft + 100 * phase + 'px';
}

Expand All @@ -526,15 +539,16 @@ window.addEventListener('scroll', updatePositions);
document.addEventListener('DOMContentLoaded', function() {
var cols = 8;
var s = 256;
for (var i = 0; i < 200; i++) {
// Reduced the number of pizza images appended to 25
for (var i = 0; i < 25; i++) {
var elem = document.createElement('img');
elem.className = 'mover';
elem.src = "images/pizza.png";
elem.style.height = "100px";
elem.style.width = "73.333px";
elem.basicLeft = (i % cols) * s;
elem.style.top = (Math.floor(i / cols) * s) + 'px';
document.querySelector("#movingPizzas1").appendChild(elem);
document.getElementById("movingPizzas1").appendChild(elem);
}
updatePositions();
});
10 changes: 6 additions & 4 deletions views/pizza.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Akim's Pizzeria'</title>
<link rel="stylesheet" href="css/style.css">
<link rel="stylesheet" href="css/bootstrap-grid.css">
</head>
Expand Down Expand Up @@ -36,7 +37,8 @@ <h1>Cam's Pizzeria</h1>
</div>
<div id="callToAction" class="row">
<div class="col-md-4">
<img src="images/pizzeria.jpg" class="img-responsive"> <!-- Image credit to: http://commons.wikimedia.org/wiki/File:HK_TST_night_%E5%98%89%E8%98%AD%E9%81%93_Granville_Road_restaurant_Paisano's_Pizzeria_pizza_Dec-2013.JPG -->
<!-- compressed image file to save total bytes-->
<img src="images/pizzeria.jpg" class="img-responsive"> <!-- Image credit to: http://commons.wikimedia.org/wiki/File:HK_TST_night_%E5%98%89%E8%98%AD%E9%81%93_Granville_Road_restaurant_Paisano's_Pizzeria_pizza_Dec-2013.JPG -->
</div>
<div class="col-md-8">
<div class="row">
Expand Down Expand Up @@ -98,13 +100,14 @@ <h2 style="text-align: center">Our Pizzas!</h2>
</div>
<div id="pizzaSize" class="col-md-6">Medium</div>
<div class="col-md-12">
<input id="sizeSlider" type="range" min="1" max="3" value="2" step="1" onchange="resizePizzas(this.value)"></input>
<input id="sizeSlider" type="range" min="1" max="3" value="2" step="1" onchange="resizePizzas(this.value)">
</div>
</div>
<div id="randomPizzas" class="row">
<div id="pizza0" class="randomPizzaContainer" style="width:33.33%; height: 325px;">
<div style="width:35%">
<img src="images/pizza.png" class="img-responsive">
<!-- compressed image file to save total bytes-->
<img src="images/pizza.png" class="img-responsive">
</div>
<div style="width:65%">
<h4>The Udacity Special</h4>
Expand Down Expand Up @@ -141,4 +144,3 @@ <h4>The Cameron Special</h4>
</body>

</html>
</html>