Code Organization Practice

Getting Setup with the Practice Exercises

To get setup with these practice exercises:

  1. If you have not already downloaded the practice files, you can get them from https://github.com/zgordon/javascript-explained

  2. Open the folder "06-organization." It should have a "starter" folder and a "completed" folder.

  3. Open the "starter" directory in your code editor. You should then see a separate folder for each practice exercise. Navigate into the appropriate folder for each exercise.

The Practice Exercises

  1. Run the following command to setup a new package.json file: npm init -y. Test that this creates a package.json file.

  2. Run the following command to install Parcel: npm install parcel-bundler --save-dev. To test that this worked, open the package.json file and make use you see parcel-bundler listed under devDependencies.

  3. Open the package.json file and update the scripts section to the following:

    "scripts": {
    "dev": "parcel src/index.js",
    "build": "parcel build src/index.js"
    },
  4. Create a directory called src and place a new file in it called index.js. Display a simple console.log('Testing') message in the index.js file.

  5. Run the command npm install. This will make sure Parcel is installed. Then run npm run dev. This should create a new folder called dist with an index.jsfile in it. Type Crt + C to stop Parcel.

  6. Create a new file called index.html in the root of your folder. Add a script tag to link to the src/index.js file.

  7. Update the package.json script to use the index.html as the source. Run npm install and then npm start. Test by opening the URL for the local server (likely http://localhost:1234) in the browser and look for Testing in the console.

    "scripts": {
    "start": "parcel index.html",
    "build": "parcel build index.html --public-url='./'"
    },
  8. Run npm install and then npm run build. Test by opening the dist/index.html in the browser and looking for Testing in the console.

  9. Create a new JavaScript file in the src folder called config.js. Include the following code:

    const config = {
    name: `Test Site`,
    };
    export default config;
  10. Open the src/index.js file and import the config.js file into it. Then display the name of the site in a console log like so: console.log(`Site Name: ${config.name})`. Then run npm install followed by npm run build and open the index.html file in the browser. You should see the following in the console: Site Name: Test Site.

  11. Create a new directory called components and place a folder in that called header. Finally place an index.js file in the header folder. Import the config file and create a function called Header() that returns the following: <h1>${config.name}</h1>. Export the Header() function.

  12. Import the Header component into the src/index.js file. Assign the Header() value to a variable called markup. Then have that markup render to the index.html page inside of the <div id="app"></div> container. Finally, run npm init and npm run build and test in the browser. You should see the header with the site name display on the page.

  13. Create a new folder called posts inside of the components folder. Place an index.js inside the posts folder. Create a variable called posts that contains an array of three post objects like the following:

    const posts = [
    {
    title: `Hello JavaScript`
    },
    {
    title: `Hello Code Organization`
    },
    {
    title: `Hello Tooling!`
    }
    ];

    Then create a function called Posts() that creates a variable called markup that includes an unordered list with each of the posts in it. Have the function return the markup. Then export the Posts() function. Finally, import Posts() into the src/index.js file. Add the value from Posts() to the markup variable after the Header() information. Then run npm install and npm run build . When you open index.html in the browser, you should see the header and the posts listed.

  14. Now break the posts variable from the posts component out into it's own file at src/data.js. Add a unique ID to each post. Export the variable from data.js and import it into the src/index.js file. Then pass the post data into the Posts(data) function. Finally, modify the src/components/posts/index.js file to get the posts from a parameter rather than a variable in the page. Run npm install and npm start to test.

  15. Turn each of the titles in the Posts component into links like the following: <li><a data-id="${post.id}" href="#">${post.title}</a></li>. Also add an ID of "posts" onto the unordered list where all the posts appear. Run npm install and npm start to test.

  16. Now add a new function called initPosts() to the posts component. Have this function get all posts from the page and attach an event listener called showPost(). Have showPost() prevent the default click behavior and then get the value of the title and log it to the console. Export initPosts() as a named export and then import it into src/index.js and call the function after the posts have been loaded to the page. You should now see the title of a post display in the console when you click on it.

  17. Inside the Posts component, create a new function called clearPosts(). This will be responsible for clearing the list of posts so a single post can be displayed. Select the unordered list of posts and assign it to the variable posts. Then remove the posts using the following format:

    posts.parentElement.removeChild(posts);

    Finally, call clearPosts() from inside the showPost() function. This should create the behavior where clicking on a post will log the post title to the console and then clear the posts from the page.

  18. Create a new folder under components called post. Place an index.js file inside of it. We will use this for displaying a single post. Create a function called Post() and set it as the default export. Have the function take an id as a parameter and log it out to the console. Then import this Post() function into the Posts component. Call it from inside the showPost() function like so:

    function showPost(e) {
    e.preventDefault();
    clearPosts();
    Post(this.dataset.id);
    }

    Notice that we are clearing the posts then passing the ID to the Post component. To test, click on a post. The posts should clear and the Post ID should be logged to the console.

  19. Open the data.js file and add in a content property to each post with some example markup like so:

    {
    id: 1,
    title: `Hello JavaScript`,
    content: `<p>Lorem to the ipsum</p>`
    }

    Then import the data file into the Post component. Inside of the Post component, find the correct post based on the ID using something like this:

    const post = data.find(post => id == post.id);

    Finally, log the correct post object to the console. To test, click on a post and the post object should be logged in the console.

  20. Open the Post component. Instead of logging the post object to the console, create the following markup for the post and add it to the page under the site title.

    <article id="post">
    <h1>POST TITLE</h1>
    <div>POST CONTENT</div>
    </article>

    To test, click on a post from the list of posts and the full post should be displayed.

  21. Open the Header component and change the markup to look like this:

    <header id="site-header">
    <p><a href="#">SITE NAME</a></p>
    </header>

    Then create a function called initHeader() that gets the header link and attaches a click event listener that calls a function called goHome() when clicked. Have the goHome() function prevent the default link behavior and simply log to the console a message Go Home!. Now import the initHeader() function into the main src/index.js file and call it before we call initPosts(). To test if this all works, click on a post and see the full post displayed. Then click on the header and see a message to Go Home!.

  22. Now we are going to modify the components so they stand on their own even more. To start, let's open config.js file and we are going to add a property to it called container with the value of document.querySelector(`#app`). This way anytime we want to reference the main container for our code it is in one place. This will let us easily change it if we ever need to in the future.

  23. Now open the Header component. Rather than have the Header() function return markup, have it load the markup directly to the page. Use config.container as the container to add the markup to. After you load them to the page, call initHeader() from within the Header() function at the very end. Then open the src/index.js file and remove all of the code and replace it simply with this:

    import Header from "./components/header";
    Header();

    When you load the site now you should just see the header display. When you click on the header it should display a message in the console to Go Home!.

  24. Now let's look at making the Posts component stand on its own. First, import config and data directly into the Posts component file. Then modify the Posts() function to no longer take a parameter and use data instead of posts to map over. Also have the Posts() function add the posts to the page rather than return markup. Use config.containers.insertAdjacentHTML() to do this. Finally, call initPosts() at the very end of the Posts() function. Then update the src/index.js file to import Posts() and call it right after Header(). If everything works you should see the posts displayed under the header. When you click on a post it should still display the single post.

  25. Now we are going to get the link in the header component working. First open the post component and create a new function that you export called clearPost(). It should look something like this:

    export function clearPost() {
    const post = document.querySelector(`#post`);
    if (post) post.parentElement.removeChild(post);
    }

    Then open the Posts component and make sure the clearPosts() function is exported and has a similar conditional check to only try to remove the posts if they are actually on the page.

    export function clearPosts() {
    const posts = document.querySelector(`#posts`);
    if (posts) posts.parentElement.removeChild(posts);
    }

    Now open the header component and import the clearPost function from the post component and Posts and clearPosts from the posts component:

    import config from "../../config";
    import { clearPost } from "../post";
    import Posts, { clearPosts } from "../posts";

    Finally, update the goHome(e) function to prevent the default behavior then clear any single posts as well as any lists of posts. Finally call Posts() to display the list of posts. Now when you click on a post, the entire post should show. Then when you click on the header the full list of posts should display again.

  26. In this step we are going to add a footer component to our project. Create a new footer folder in the components folder. Place an index.js file in it that exports a Footer() function. For now, just have the Footer()display the name of the site from the config file inside of a <footer> tag with an ID of site-footer. Then have it added to the page below the rest of the content. Import the Footer() component into the main src/index.js file and call it after Posts(). To test, load the page and see the footer display at the bottom of the page. When you click to view a post the location of the footer will change. We will fix that in the next step.

  27. Now update the Posts and Post components to load after the header rather than at the end of the main config.container.

  28. Add a copyright symbol and the current year before the site name in the footer. You can use the following code to get the current year.

    const today = new Date();
    const year = today.getFullYear();
  29. Create a new file called index.css inside the src folder with the following simple CSS rules:

    body {
    background: #c3f3eb;
    font-size: 2rem;
    font-family: sans-serif;
    margin: 0;
    padding: 0;
    }
    a {
    color: #222;
    }
    #post,
    #posts {
    margin: 2rem;
    }

    Now inside the src/index.js import the CSS file with the following line:

    import "./index.css";

    Finally, open the index.html file and link to the new CSS file in src/index.css like so:

    <link rel="stylesheet" type="text/css" href="src/index.css" />

    To test, run Parcel and you should see the CSS display.

  30. Now add an index.css file to the header component folder. Include the following CSS:

    #site-header {
    background: #dcccff;
    padding: 1rem;
    font-size: 3rem;
    text-align: center;
    }
    #site-header a {
    color: #222;
    text-decoration: none;
    text-transform: uppercase;
    }

    Then open the header index.js file and import the CSS:

    import "./index.css";

    Now we will do the same for the Footer component. Open the footer component and add an index.css file to it with the following styles:

    #site-footer {
    background: #f8f991;
    bottom: 0;
    height: 2rem;
    font-size: 1rem;
    position: absolute;
    padding: 0.5rem;
    text-align: center;
    width: calc(100% - 1rem);
    }

    Now open the Footer index.js file and import the CSS file the same way as with the Header component. To test, run Parcel and open the site in the browser. You should see something like this:

What Next?

At this point you should have some comfort designing a JavaScript project using component architecture. In the next step we are going to build a larger project using everything we have learned up to this point.