Project 4: Impossible Questionnaire (JS on Rails)

The Backstory

For this project, we dove into vanilla JS. I spent a year coding in JS when I was at NYU Tisch’s ITP program, but almost exclusively within the p5js library. So, a lot of it felt familiar, yet a lot of it really, really didn’t. Specifically manipulating DOM elements wasn’t something we really did using p5. It’s more of a canvas for creating abstract art with code than it is a resource for building web applications.

Early on in the JS curriculum a lot of my fellow cohort students noticed that the example projects that were shown for JS all seemed really impressive and kind of unattainable. I took a closer look at them, however, and I realized that half of them were actually made with p5js, so I considered going down that route.

You can do really amazing visual things with p5js, and it’s also really comfortable territory for me. So in the beginning I considered revamping one of my old projects where I manipulated pixel data to demonstrate the expanding universe with red shift. I wanted to remake it so it would iterate through an API of space images to further demonstrate the concept, but it was hard to get a consistent stream of space images that weren’t illustrations, even from NASA’s APIs.

Spacebar for fullscreen.
Press 1 or 2 to toggle redshift or blueshift. Press 0 to reset.
Or just wait for the slide show.

I made a version that iterates through some Hubble images (gitHub link here) via their “print shop” collection in their API, but again, the images need to be consistently uncolorized and of a similar scale for the demonstration to work. Anyways, in the end I decided to pursue a fully vanilla JS project. Not because of any failure or inability to execute my p5 project, but ultimately I wouldn’t have learned anything from rehashing old projects. Oh, and if you do try to run the one on gitHub, it only works using liveserver due to some cors issues I’m still figuring out.


A very leading Donald Drop performance review
 

Impossible Questionnaire

I really don’t remember how I came up with the idea, but ultimately I built a whole project around a punch line: A Donald Trump exit survey where any answers that weren’t flattering ran away from you.

The requirements of the project are to make a single-page app where all of its functionality is built with vanilla JS with Rails as the backend. Here is the github repo link. I think it came out pretty well. I’m particularly proud of this one.

Here is a full video walkthrough of it:

 

Building it

The project started with a Rails backend, piggybacking off our Rails knowledge from out last projects. That part was pretty simple once I had really decided how my data structure was going to work. One of the requirements is that our domain models have a has_many : belongs_to relationship.

For mine, a Questionnaire has many Questions and a Question belongs to a Questionnaire.

screen-11-23-2020-03.jpg
 
screen-11-23-2020-05.png
 

Creating the schema took a lot of thought and consideration. It’s also a really important decision when building an app. Any changes you make to your schema have a tendency to permeate into the rest of your app, so it’s helpful to have a solid database foundation before moving forward. The Questionnaire model is easy enough. It’s the Question model that took more thought, because each answer would have their own unique attributes. I needed a way of identifying which was the correct answer, most importantly. I considered making an Answer model to take care of the answer attributes, but that was just too daisy-chained. In the end I created ActiveRecord columns for “answer_1, answer_2, answer_3, answer_4, correct_answer”. A question object would also have “content”, meaning the question itself. Here’s my schema:

 

Once I felt good about my data structure, I had to convert my Rails objects into JSON objects from their respective controllers. I decided it made the most sense to have a single API (the “Questionnaires”) take care of sending all the data. So I made my Questionnaires json have access to all of the relevant Question data that each Questionnaire would have.

 

I should also note that I never actually used the “show” action, but I made it in the beginning anticipating the possibility that I might. I also opted not to use a Serializer for my json data because I wasn’t really doing anything that complicated, and in this particular case it would’ve been more trouble than it would be worth.

Facing my CSS Demons

I had been somewhat successfully fumbling through CSS through most of the curriculum so far, but this time I decided to take a long, hard look at it and really try to understand it. It’s too valuable an asset to ignore. I spent an hour reading about Bootstrap during the last project, but I was overwhelmed by it and put off understanding it, yet again.

I think part of my problem was that I fundamentally didn’t understand what the hell Boostrap was. I only knew that people kept talking about it as an “easy” way to style a web app. Once I actually read through their getting started docs (their docs are super user-friendly btw), it was much less intimidating.

Honestly, all Bootstrap is, is a fancy stylesheet that you can just link to with a URL. That’s all it is. It’s actually a little uncomfortable to use because you don’t actually have the assets to see what’s going on under the hood. But their documentation is so clear, it really is an amazing resource to have.

More CSS Demons

Demons would be an exaggeration at this point, because I was feeling more comfortable, but I did spend a lot of time on MDN CSS docs just figuring out how to manipulate various style attributes. The sort of “main event” of the app is very much dependent on style attributes to animate the fleeting answers of the questionnaires.

Actually, before I even decided to proceed with this project, I had to make sure I would be able to accomplish some sort of interactive effect that would make my punchline plausible. I started with simple “:hover: attribute on my stylesheet. I should back up: in addition to using bootstrap, I used another .css file where I could write attributes that would override bootstrap attributes and help me create the desired effects I was going for.

So, I started with a simple css hover attribute that looked like this:

screen-11-23-2020-09.png
 

This is basically saying (in pseudo-code) “When you hover over the wrong answer, declare that elements position as relative, move it by 150px from the top and from the left, bring it above the other elements, and make its background white. This accomplishes moving all of the wrong answers in a singular direction, but it feels less alive when they’re all going to the same relative place.

You’ll notice that commented out is also “visibility: hidden.” I also experimented with just hiding that element entirely when you hovered over it. But I also found it to be glitchy, so I also used it in combination with moving the element out of the way, and that did a nice thing. But, in the end, I did the final animation with JS.

function animateWrongAnswers() {
    const wrongAnswers = document.querySelectorAll(".input-group[answertype=wrong]")

    wrongAnswers.forEach(answer => {
        answer.querySelector(".input-group-text").addEventListener('click', e => {
            e.target.checked = false;
        })
        
        answer.addEventListener('mouseover', e => {
            let plusOrMinus = Math.random() < 0.5 ? -1 : 1;
            let randomCoord = Math.random() * 200 * plusOrMinus;

            e.target.parentElement.style.left = `${randomCoord}px`;
            e.target.parentElement.style.top = `${randomCoord}px`;

            setInterval(function() {
                resetCoords(e);
            }, 2000);

            function resetCoords(e) {
                e.target.parentElement.style.left = "0px";
                e.target.parentElement.style.top = "0px";
            }
        })
    })
        
}

Building with OOJS

This was only a small portion of the work that went into this project — a very small part. I also only did this at the very end of my work. Where to even begin?

Fortunately, I did have experience with object oriented Javascript from my p5js days. Here is my very first OOJS project:

Click to create planets. Click on planets to delete them.

The first thing I did after making a basic web page skeleton with bootstrap was populate the DOM with an actual questionnaire, because what good would the rest of the functionality be if I couldn’t even display one questionnaire?

I created a Questionnaire class:

class Questionnaire {

    constructor(obj) {
        this.id = obj.id;
        this.title = obj.title;
        this.result = obj.result;
        this.featured = obj.featured;
        this.questions = obj.questions;
        this.description = obj.description;
        Questionnaire.all.push(this)
    }

    static all = [];
...
}

I deliberated for a while about whether or not I wanted to create a Question class or if my Questionnaire class should be in control of everything. I actually did start building my app out with just the Questionnaire class, but in the end I felt it made more sense to create a Question class after all.

class Question {
    constructor (obj) {
        this.id = obj.id;
        this.content = obj.content;
        this.answers = {
            answer_1: obj.answer_1,
            answer_2: obj.answer_2,
            answer_3: obj.answer_3,
            answer_4: obj.answer_4, 
            correct_answer: obj.correct_answer
        }
    }
  ...
}

Now that I had my foundation for dealing with Questions and Questionnaires, I also needed a way to get data from the backend, which I did with an Api class:

class ApiService {

    constructor(baseUrl) {
      this.baseUrl = baseUrl;
    }
  ...
}

This handles all of the fetch requests to and from the database.

Widdling Away at the Functionality

Now I had my objects set up and I just wrote class method after class method getting features to work one at a time. Here was my order of operations (grossly oversimplified of course) in how I built out the app.

  1. Display a single questionnaire, and make that method reusable to other questionnaires

  2. Fetch and receive all questionnaires

  3. Display all questionnaires

  4. Be able to click on those displayed questionnaires and view that single questionnaire (using the method from step 1)

  5. Create a questionnaire form

  6. Have that form send a post request to the backend, then display that questionnaire (again using the method from step 1)

  7. Build out the navbar buttons (and attach them to previously built methods, and soon-to-be-built methods)

  8. Make a functional search bar that displays a filtered index (recycling the method built from step 3)

  9. Filter out questionnaires with the “featured” attribute and populate the dropdown with them

  10. Have the dropdown items navigate to the correct questionnaire when you click on them

  11. Make a cool method that makes the non-desired answers run away

That’s how I created it, just focusing on one task at a time. This really was a heady project. There’s something about Javascript that makes it a very involved intellectual exercise every step of the way. Me, personally, I really enjoy coding in Javascript. I think it’s a lot of fun, and there’s a lot of freedom in how limitless it is. I do acknowledge, however, that it can be just as equally frustrating.

It really is impossible to go into all the hurdles and triumphs that happened during this project. It was an emotional adventure, but coding often is. Here is my github repo once again if you’re curious. And please do feel free to comment or reach out to me with any questions.