Sitemap
Chris Houghton

Chris Houghton

FlatIron School JS

7 min readNov 12, 2021

Project: Spooky Season

Did you know that JavaScript is used by 97.6% of all websites, according to W3Techs? Now you do, it is estimated 1.7 billion websites exist, that means 1.66 billion websites use JavaScript. And now that you know, you know why it is such a good and almost necessary language to learn if you want to put your apps on the web. This blog is going to showcase some of what I thought were the more interesting parts of a web application I developed while learning this highly used interactive website creating language.

Press enter or click to view image in full size
Image created with the project app.

To begin with let’s talk about the backend. For the backend we are using Rails as an API only to send and receive information from the database, to do this we open our terminal and Generate a new Rails API using

rails new <my_app_name> --database=postgresql --api

Next we create our Rails migrations, models, controllers, routes and associations the same way if we were to create a rails application

class Card < ApplicationRecordhas_many :card_backgroundshas_many :backgrounds, through: :card_backgroundsend
def createcard = Card.new(name: card_params[:name])end
Rails.application.routes.draw doresources :cards, only: [:index, :create, :show]end

To send data to the frontend Rails requires us to use render json (JavaScript Object Notation) in the controller action. Here we send the card object created as_json to include only specific attributes of the object, the cards index action is a simple render scheme

render json: cards.as_json( only: [:id, :name])

Where as the show action for individual cards is complex comparatively, here we are asking to for the cards associations specific attributes.

render json: card.as_json( only: [:id, :name],  include: {    element_positions: {      only: [:element_id, :image_url, :xPosition, :yPosition,    :xOffset, :yOffset, :imageSize, :name]    },
elements: {
only: [:id, :image_url] }, backgrounds: { only: [:id, :name] }}

Next we are going to move on to the Frontend where all the action is. First thing we are going to add an event listener for the DOM, this will run anything we put inside the method after the DOM is loaded. This ensures if we are querying an element it will be found by searching for it after it is loaded and not before, woo.

document.addEventListener('DOMContentLoaded', () => {console.log('dom is loaded');}

Outside this event listener a whole bunch of functions and classes with functions and logic will be written, inside the event listener the functions will be called. The first of these functions will be a fetch call to the database to get elements that can be manipulated by the user in the app. fetch calls to the database and receives a response, we take the response and .then convert it to json and .then iterate over the results calling another function to operate on the data. From adding elements to the dom, event listeners, and functions to manipulate and create.

function getCardItems(database,func) {  fetch(database)  .then(response => response.json())  .then(items => {    for (const item of items) {      func(item)    }  })

In the ‘Spooky Season’ app a fetch call is made to the database to get card elements and backgrounds to create halloween greeting cards, the call receives a bunch of elements (response => response.json()) each representing a different img ( for (const item of items)) that is then added to the DOM the user can then click on and add to a card. There is also a fetch call for backgrounds and already created cards.

Get Chris Houghton’s stories in your inbox

Join Medium for free to get updates from this writer.

When a user selects a background the background img is then added to the DOM, it is shown as the background to the ‘card’. The user can then select ‘elements’ and add them to the DOM and card as well, after rendered to the ‘card’ the user can then move and resize the elements to their artistic liking.

Which brings me to the part of the project I was most interested in, how to select and element on the screen, move and set element wherever the user sees fit according to their artist vision and have element not interfere with other elements in the card div.

To begin I had to be able to select an element and have that elements position be the same as the mouse position, I toyed with a few different approaches including element.style.left/top and the elements x/y position using

.getBoundingClientRect-method returns a DOMRect object providing information about the size of an element and its position relative to the viewport.

which worked but made for a pain when it came to moving multiple elements, one element selected would move while all others would act strange or not move at all, while the originally clicked element would be the only element to behave in the manner intended.

After some study I came across and decided to settle on was the css transform property. This property allowed me to move an element around the screen based on the elements original position from being loaded to the DOM, save the elements new position and distance from the original position to the elements JS object. When element is clicked on again a function loads this information to the translate property and allows us to continue moving the object. Let’s say for instance the original position was 0px and 0px, top left corner, first variable on the x axis and second on the vertical y axis. Using translate we want to move the element 50px on the x and 50px on the y.

transform: translate(50px,50px);

Now the position of the element would be 50px in and 50px down.

courtesy of https://www.w3schools.com/css/css3_2dtransforms.asp

If I wanted the element to be 100px in and 100px down I would have to put whatever I want the transformation to be from the original position, I emphasize this only because I originally thought the translated position became the new position of the element, but this is not the case. The position of the element will always be the original position when loaded to the DOM.

transform: translate(100px,100px);

So now I can move and element from its original position using transform. All I have to keep track of is the value of the x/y movements from the original position and save it to the objects I have created when adding element to the ‘card’ and DOM. When element is selected again I can continue the transformation of original element position and save the amount transformed when deselected. boom.

The other dilemma was snapping the element to the position of the mouse upon selection. To counter this we need an offset of the mouse position and the element position, I know even as I write this it is sounding confusing but bear with me it all makes sense in the end.

Let’s say the element position is back at the 50px on xAxis and 50px on yAxis mark again, and the mouse position when we click is at 59px on x Axis and 57px on yAxis. Since the elements position is transformed based on the mouse position we will get an ugly snap of the element position to the mouse position. In steps the offset, the element is what is getting transformed, the mouse position is the basis of the transformation, so to get an accurate position to transform we need to subtract the element position x from the mouse position x, same with the y positions and transform the element based on the mouse position minus the offset.

currentX = e.clientX - (e.clientX - element.x)
or
mousePosition - (mousePosition - lastKnownTranslatePosition)

So that no matter where I click on the element, the transform position is based on the offset of the mouse position. This almost seems unnecessary if given the last know transform position, but still bearing with me the current transform position is based on the current mouse position relative to where the click on the element was activated. This is all to literally show a more fluid movement when clicking and moving the element, no snappin, fluid.

Here is the code for study.

The last thing worth mentioning in this blog is sending fetch calls to create new objects and save them to the database. Here we send a fetch using the RESTful HTTP verb post so the rails API can intercept and interpret this as a create action for the cards controller, the Rails API will create based on the parameters sent then send back the newly created object which we will format to json and add to the DOM.

fetch("http://127.0.0.1:3000/cards", {  method: "POST",  headers: {"Content-Type": "application/json"},  body: JSON.stringify(bodyData)}).then(response => response.json()).then(json => {  getCardLoadout(json)})}

To sum up, there is an Rails API that handles the database, sends objects, gets information and creates objects. There is the JS frontend that handles the DOM and user interactions and dynamically change a the web page allowing the user in this example to make weird, goofy halloween cards. Here at the end I can say I greatly enjoyed building this app while learning Javascript, and also like I think I say in all of my project blogs it was extremely difficult but very rewarding.

Thanks for reading.

--

--