The
Florida Man meme is an internet phenomenon due to the frankly bizarre headlines that come up every time someone does an internet search for the term "florida man". Honesely, I'm not so sure what's special about this swampy state in the USA, but it is what it is.
Just to get in some practice with VueJS, today we will be making a Florida Man headline generator. It won't be much - it will generate eight random headlines every time the page refreshes.
I plan to use images - but on the off-chance you don't want to, or want to keep images at a minimum, just consider using this one.
|
floridaman.jpg |
Let's get cracking!
The starting HTML is here. Notice that we have a remote link to Vue.
<!DOCTYPE html>
<html>
<head>
<title>Florida Man</title>
<style>
</style>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.18/vue.min.js"></script>
<script>
</script>
</body>
</html>
For this, in the HTML, we want a div in there with a class of
floridamanApp. And in the JavaScript, we make a call to create a new Vue object.
<!DOCTYPE html>
<html>
<head>
<title>Florida Man</title>
<style>
</style>
</head>
<body>
<div id="floridamanApp">
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.18/vue.min.js"></script>
<script>
var app = new Vue
(
);
</script>
</body>
</html>
Within that div, we have eight divs, each styled using the CSS class
headlineContainer. Each one has an inner div styled using the CSS class
headline. And in turn, each div has a h1 tag.
<div id="floridamanApp">
<div class="headlineContainer">
<div class="headline">
<h1></h1>
</div>
</div>
<div class="headlineContainer">
<div class="headline">
<h1></h1>
</div>
</div>
<div class="headlineContainer">
<div class="headline">
<h1></h1>
</div>
</div>
<div class="headlineContainer">
<div class="headline">
<h1></h1>
</div>
</div>
<div class="headlineContainer">
<div class="headline">
<h1></h1>
</div>
</div>
<div class="headlineContainer">
<div class="headline">
<h1></h1>
</div>
</div>
<div class="headlineContainer">
<div class="headline">
<h1></h1>
</div>
</div>
<div class="headlineContainer">
<div class="headline">
<h1></h1>
</div>
</div>
</div>
We will fill up the first div with some sample content within the h1 tag. The curly bracer pairs are placeholders.
<div class="headlineContainer">
<div class="headline">
<h1>Florida Man {{ }} {{ }} {{ }} {{ }} for {{ }} {{ }}!</h1>
</div>
</div>
Now, admittedly, this isn't much. This needs some styling.
For the
headlineContainer CSS class, we provide a specific width and height, set the
float property to
left and give it a top and left margin of 5 pixels. That's for layout.
<style>
.headlineContainer
{
width: 500px;
height: 200px;
float: left;
margin: 5px 0 0 5px;
}
</style>
We then give it round corners and a thick
grey border, make sure the
background-size property is set to
cover (because there will be a background image later on) and ensure that it's set to a vintage coloring by applying grayscale at 100%. Lastly, we set the
overflow property to
hidden in order to ensure that the HTML contents are neatly handled.
<style>
.headlineContainer
{
width: 500px;
height: 200px;
float: left;
border-radius: 10px;
border: 5px solid rgba(100, 100, 100, 1);
margin: 5px 0 0 5px;
filter: grayscale(100%);
background-size: cover;
overflow: hidden;
}
</style>
For the
headline CSS class, we set width and height at 100%, float it left and apply a translucent
black background.
<style>
.headlineContainer
{
width: 500px;
height: 200px;
float: left;
border-radius: 10px;
border: 5px solid rgba(100, 100, 100, 1);
margin: 5px 0 0 5px;
filter: grayscale(100%);
background-size: cover;
overflow: hidden;
}
.headline
{
width: 100%;
height: 100%;
float: left;
background-color: rgba(0, 0, 0, 0.5);
}
</style>
This looks OK-ish. For
headlineContainer, the
overflow property has been set to
hidden, so you won't see any ugly seams at the edges. The layout appears to be working nicely.
For the h1 tag, we add some padding and specify the width. Center everything. Make the words captialized.
.headline
{
width: 100%;
height: 100%;
float: left;
background-color: rgba(0, 0, 0, 0.5);
}
.headline h1
{
text-transform: capitalize;
text-align: center;
padding: 10px;
width: 480px;
}
Then we make the text all
white and use the
text-shadow property to give it a
black outline.
.headline h1
{
text-transform: capitalize;
text-align: center;
color: rgba(255, 255, 255, 1);
text-shadow: 1px -1px 0 rgba(0, 0, 0, 1), -1px 1px 0 rgba(0, 0, 0, 1), -1px -1px 0 rgba(0, 0, 0, 1), 1px 1px 0 rgba(0, 0, 0, 1);
padding: 10px;
width: 480px;
}
Yeah! Looks great!
That's out of the way. We are going to do some JavaScript next. For Vue, we have to specify
data and
methods. And before we forget, set the
el property to interact with
floridamanApp.
<script>
var app = new Vue
(
{
el: "#floridamanApp",
data:
{
},
methods:
{
}
}
);
</script>
For
data, we have
headlines and
wordTypes, both arrays. We will deal with
headlines in a bit, but let's first take a look at
wordTypes. Each element is an array of words. You can fill this up with whatever words you wish. I've done a few right here.
data:
{
headlines:
[
],
wordTypes:
{
nouns:
[
"alligator",
"businessman",
"cop",
"dragonfly",
"ewe",
"florist",
"girl",
"hamster",
"iguana",
"joker"
],
nouns_plural:
[
"alligators",
"businessmen",
"cops",
"dragonflies",
"ewes",
"florists",
"girls",
"hamsters",
"iguanas",
"jokers"
],
numbers:
[
"three",
"six",
"thirteen",
"nineteen",
"twenty-three",
"fifty-nine",
"eighty-seven"
],
units:
[
"bags",
"crates",
"pounds",
"pairs",
"consignments"
],
units_time:
[
"days",
"hours",
"minutes",
"weeks"
],
actions:
[
"antangonize",
"brutalize",
"chase",
"kill",
"marry",
"proposition",
"question"
],
actions_past:
[
"antangonized",
"brutalized",
"chased",
"killed",
"married",
"propositioned",
"questioned"
],
actions_plural:
[
"antangonizes",
"brutalizes",
"chases",
"kills",
"marries",
"propositions",
"questions"
],
actions_active:
[
"antangonizing",
"brutalizing",
"chasing",
"killing",
"marrying",
"propositioning",
"questioning"
],
descriptors:
[
"anxious",
"articulate",
"blasphemous",
"blind",
"caustic",
"cowardly",
"dead",
"dramatic",
"enormous",
"epic",
"friendly",
"furious",
"generous",
"gorgeous",
"heavy",
"huge",
"impassive",
"irate",
"jovial",
"jumpy",
"kind",
"knowledgeable",
"lascivious",
"livid",
"monstrous",
"mysterious"
]
}
},
Now, for headlines. Each element in headlines is an object consisting of key-value pairs. These keys should have an equivalent to the keys in
wordTypes, and the values are arrays of empty strings. Note that
descriptors has two empty strings instead of one. You'll see why later on.
headlines:
[
{
actions_plural: [""],
descriptors: ["", ""],
nouns: [""],
numbers: [""],
units_time: [""]
}
],
Let's create some methods! We begin by having a
fillHeadlineArray() method. It has a parameter,
headlineIndex. This is a pointer to the element in
headlines that we want to change, or fill, as it were. There is only one element in
headlines right now, but that is going to change later.
methods:
{
fillHeadlineArrays: function(headlineIndex)
{
}
}
We first get
keys by taking all the keys of the
wordTypes array. What we want to do is use a
For-each loop to iterate through the keys.
methods:
{
fillHeadlineArrays: function(headlineIndex)
{
var keys = Object.keys(this.wordTypes);
keys.forEach(
(wordType) =>
{
}
);
}
}
And in it, search the current element of
headlines for that particular key. If it exists...
methods:
{
fillHeadlineArrays: function(headlineIndex)
{
var keys = Object.keys(this.wordTypes);
keys.forEach(
(wordType) =>
{
if (this.headlines[headlineIndex][wordType])
{
}
}
);
}
}
...fill each empty string in that particular array, with a randomly selected element from that particular array in
wordTypes. For this, we use a
For loop and the
getOne() method.
methods:
{
fillHeadlineArrays: function(headlineIndex)
{
var keys = Object.keys(this.wordTypes);
keys.forEach(
(wordType) =>
{
if (this.headlines[headlineIndex][wordType])
{
for (let i = 0; i < this.headlines[headlineIndex][wordType].length; i++)
{
var newWord = this.getOne(this.wordTypes[wordType]);
this.headlines[headlineIndex][wordType][i] = newWord;
}
}
}
);
}
}
This is the
getOne() method. It accepts an array,
arr. We just randomly pick one element from
arr and return it.
methods:
{
fillHeadlineArrays: function(headlineIndex)
{
var keys = Object.keys(this.wordTypes);
keys.forEach(
(wordType) =>
{
if (this.headlines[headlineIndex][wordType])
{
for (let i = 0; i < this.headlines[headlineIndex][wordType].length; i++)
{
var newWord = this.getOne(this.wordTypes[wordType]);
this.headlines[headlineIndex][wordType][i] = newWord;
}
}
}
);
},
getOne: function(arr)
{
return arr[Math.floor((Math.random() * arr.length))];
}
}
Now in Vue, set
created.
methods:
{
fillHeadlineArrays: function(headlineIndex)
{
var keys = Object.keys(this.wordTypes);
keys.forEach(
(wordType) =>
{
if (this.headlines[headlineIndex][wordType])
{
for (let i = 0; i < this.headlines[headlineIndex][wordType].length; i++)
{
var newWord = this.getOne(this.wordTypes[wordType]);
this.headlines[headlineIndex][wordType][i] = newWord;
}
}
}
);
},
getOne: function(arr)
{
return arr[Math.floor((Math.random() * arr.length))];
}
},
created: function()
{
}
Let's use a
For loop to iterate through
headlines, running the
fillHeadlineArrays() method for each one. Remember, right now, and I know I risk sounding like a broken record at this point, there is only one element in
headlines.
created: function()
{
for (let i = 0; i < this.headlines.length; i++)
{
this.fillHeadlineArrays(i);
}
}
In the HTML, we need to put in the references in the placeholder bracers. Note that "descriptors" appears twice. That's because the
descriptors array in the first (and only) element of
headlines has two elements. So the first one is referenced using index 0, and the second using index 1.
<div class="headlineContainer">
<div class="headline">
<h1>Florida Man {{ headlines[0]["actions_plural"][0] }} {{ headlines[0]["descriptors"][0] }} {{ headlines[0]["descriptors"][1] }} {{ headlines[0]["nouns"][0] }} for {{ headlines[0]["numbers"][0] }} {{ headlines[0]["units_time"][0] }}!</h1>
</div>
</div>
Let's see what we get!
Let's make another headline. This time, the elements of headlines are indexed using 1, whereas in the previous one, they were indexed using 0.
<div class="headlineContainer">
<div class="headline">
<h1>Florida Man {{ headlines[0]["actions_plural"][0] }} {{ headlines[0]["descriptors"][0] }} {{ headlines[0]["descriptors"][1] }} {{ headlines[0]["nouns"][0] }} for {{ headlines[0]["numbers"][0] }} {{ headlines[0]["units_time"][0] }}!</h1>
</div>
</div>
<div class="headlineContainer">
<div class="headline">
<h1>Florida Man {{ headlines[1]["actions_past"][0] }} by {{ headlines[1]["nouns"][0] }} for {{ headlines[1]["actions_active"][0] }} {{ headlines[1]["nouns"][1] }}!</h1>
</div>
</div>
<div class="headlineContainer">
<div class="headline">
<h1></h1>
</div>
</div>
In
headlines, we add another object. This one has a different set of word types.
headlines:
[
{
actions_plural: [""],
descriptors: ["", ""],
nouns: [""],
numbers: [""],
units_time: [""]
},
{
actions_past: [""],
nouns: ["", ""],
actions_active: [""]
}
];
Again, let's see what we get. Notice that "dragonfly" appears twice in the second headline. It's a little boring to have the same words being used, so let's take care of that.
In the
fillHeadlineArrays() method, add this line. It's a
While loop that ends only if the randomly selected word isn't already in that particular array. Do note that this will result in an infinite loop if you have more elements in that particular array than there are in the corresponding
wordTypes array!
fillHeadlineArrays: function(headlineIndex)
{
var keys = Object.keys(this.wordTypes);
keys.forEach(
(wordType) =>
{
if (this.headlines[headlineIndex][wordType])
{
for (let i = 0; i < this.headlines[headlineIndex][wordType].length; i++)
{
var newWord = this.getOne(this.wordTypes[wordType]);
while (this.headlines[headlineIndex][wordType].indexOf(newWord) != -1)
{
newWord = this.getOne(this.wordTypes[wordType]);
}
this.headlines[headlineIndex][wordType][i] = newWord;
}
}
}
);
},
This should be fine now.
Adding images
What's a headline without an image? We will add a call to the
getStyle() method in each div here, and pass in the current index of each div, as an argument.
<div class="headlineContainer" v-bind:style="getStyle(0)">
<div class="headline">
<h1>Florida Man {{ headlines[0]["actions_plural"][0] }} {{ headlines[0]["descriptors"][0] }} {{ headlines[0]["descriptors"][1] }} {{ headlines[0]["nouns"][0] }} for {{ headlines[0]["numbers"][0] }} {{ headlines[0]["units_time"][0] }}!</h1>
</div>
</div>
<div class="headlineContainer" v-bind:style="getStyle(1)">
<div class="headline">
<h1>Florida Man {{ headlines[1]["actions_past"][0] }} by {{ headlines[1]["nouns"][0] }} for {{ headlines[1]["actions_active"][0] }} {{ headlines[1]["nouns"][1] }}!</h1>
</div>
</div>
Create the
getStyle() method. What we want is to grab images. Since right now we only use
floridaman.jpg, we can put that in an array, use the
getOne() method to pick it (it will pick this image because that's the only one) and return the style string!
getOne: function(arr)
{
return arr[Math.floor((Math.random() * arr.length))];
},
getStyle: function(headlineIndex)
{
var arr = ["floridaman"];
if (arr.length == 0)
{
return "";
}
else
{
return "background: url(" + this.getOne(arr) + ".jpg)";
}
}
There you go.
But what if you want more? Then we'll have to add more. Here, I'm creating the
images array and populating it with the names of all the images sans extension. The images can be found in
this repository.
getStyle: function(headlineIndex)
{
var images = ["alligator", "businessman", "cop", "dragonfly", "ewe", "florist", "girl", "hamster", "iguana", "joker", "alligators", "cops", "girls", "hamsters"];
var arr = ["floridaman"];
if (arr.length == 0)
{
return "";
}
else
{
return "background: url(" + this.getOne(arr) + ".jpg)";
}
}
Next, we define
keys the same way we did in
fillHeadlineArrays(). And then I do a
For-each loop, going through the entire
wordTypes array.
getStyle: function(headlineIndex)
{
var keys = Object.keys(this.wordTypes);
var images = ["alligator", "businessman", "cop", "dragonfly", "ewe", "florist", "girl", "hamster", "iguana", "joker", "alligators", "cops", "girls", "hamsters"];
var arr = ["floridaman"];
keys.forEach(
(wordType) =>
{
}
);
if (arr.length == 0)
{
return "";
}
else
{
return "background: url(" + this.getOne(arr) + ".jpg)";
}
}
We do a search for
wordType in the current element of
headlines. If it's found, we do an inner
For-each loop to go through
images.
keys.forEach(
(wordType) =>
{
if (this.headlines[headlineIndex][wordType])
{
images.forEach(
(img) =>
{
}
)
}
}
);
... and then push
img into the array
arr if it is found in the list of words! Now the
getOne() method should return more than one possible value if we pass in
arr as an argument.
keys.forEach(
(wordType) =>
{
if (this.headlines[headlineIndex][wordType])
{
images.forEach(
(img) =>
{
if (this.headlines[headlineIndex][wordType].indexOf(img) != -1) arr.push(img);
}
)
}
}
);
Great! In the first headline, the script chooses between
floridaman.jpg and
cop.jpg. In the second headline, the script chooses between
floridaman.jpg,
dragonfly.jpg and
ewe.jpg.
We can add more content now in the HTML.
<div class="headlineContainer" v-bind:style="getStyle(0)">
<div class="headline">
<h1>Florida Man {{ headlines[0]["actions_plural"][0] }} {{ headlines[0]["descriptors"][0] }} {{ headlines[0]["descriptors"][1] }} {{ headlines[0]["nouns"][0] }} for {{ headlines[0]["numbers"][0] }} {{ headlines[0]["units_time"][0] }}!</h1>
</div>
</div>
<div class="headlineContainer" v-bind:style="getStyle(1)">
<div class="headline">
<h1>Florida Man {{ headlines[1]["actions_past"][0] }} by {{ headlines[1]["nouns"][0] }} for {{ headlines[1]["actions_active"][0] }} {{ headlines[1]["nouns"][1] }}!</h1>
</div>
</div>
<div class="headlineContainer" v-bind:style="getStyle(2)">
<div class="headline">
<h1>Florida Man set to {{ headlines[2]["actions"][0] }} {{ headlines[2]["descriptors"][0] }} {{ headlines[2]["descriptors"][1] }} {{ headlines[2]["nouns"][0] }} in {{ headlines[2]["numbers"][0] }} {{ headlines[2]["units_time"][0] }}.</h1>
</div>
</div>
<div class="headlineContainer" v-bind:style="getStyle(3)">
<div class="headline">
<h1>Florida Man {{ headlines[3]["actions_plural"][0] }} {{ headlines[3]["numbers"][0] }} {{ headlines[3]["units"][0] }} of {{ headlines[3]["nouns_plural"][0] }} for {{ headlines[3]["descriptors"][0] }} {{ headlines[3]["descriptors"][1] }} {{ headlines[3]["nouns"][0] }}.</h1>
</div>
</div>
<div class="headlineContainer" v-bind:style="getStyle(4)">
<div class="headline">
<h1>Florida Man is {{ headlines[4]["descriptors"][0] }} at {{ headlines[4]["descriptors"][1] }} {{ headlines[4]["descriptors"][2] }} situation involving {{ headlines[4]["descriptors"][3] }} {{ headlines[4]["nouns_plural"][0] }} and {{ headlines[4]["descriptors"][4] }} {{ headlines[4]["nouns_plural"][1] }}.</h1>
</div>
</div>
<div class="headlineContainer" v-bind:style="getStyle(5)">
<div class="headline">
<h1>Florida Man unable to {{ headlines[5]["actions"][0] }} {{ headlines[5]["nouns"][0] }} due to {{ headlines[5]["descriptors"][0] }} {{ headlines[5]["nouns_plural"][0] }}.</h1>
</div>
</div>
<div class="headlineContainer" v-bind:style="getStyle(6)">
<div class="headline">
<h1>Florida Man jailed for {{ headlines[6]["actions_active"][0] }} {{ headlines[6]["nouns_plural"][0] }} and {{ headlines[6]["actions_active"][1] }} {{ headlines[6]["descriptors"][0] }} {{ headlines[6]["nouns"][0] }}.</h1>
</div>
</div>
<div class="headlineContainer" v-bind:style="getStyle(7)">
<div class="headline">
<h1>Florida Man wins {{ headlines[7]["descriptors"][0] }} contest against {{ headlines[7]["numbers"][0] }} {{ headlines[7]["units"][0] }} of {{ headlines[7]["descriptors"][1] }} {{ headlines[7]["nouns_plural"][0] }}.</h1>
</div>
</div>
And in the headlines array.
headlines:
[
{
actions_plural: [""],
descriptors: ["", ""],
nouns: [""],
numbers: [""],
units_time: [""]
},
{
actions_past: [""],
nouns: ["", ""],
actions_active: [""]
},
{
actions: [""],
descriptors: ["", ""],
nouns: [""],
numbers: [""],
units_time: [""]
},
{
actions_plural: [""],
numbers: [""],
units: [""],
nouns_plural: [""],
descriptors: ["", ""],
nouns: [""]
},
{
descriptors: ["", "", "", "", ""],
nouns_plural: ["", ""]
},
{
actions: [""],
nouns: [""],
descriptors: [""],
nouns_plural: [""]
},
{
actions_active: ["", ""],
nouns: [""],
descriptors: [""],
nouns_plural: [""]
},
{
numbers: [""],
units: [""],
nouns_plural: [""],
descriptors: ["", ""]
}
],
Our final product!
Thanks for reading!This was totally awesome. And yes, really frivolous. Probably not really something you'd want to use VueJS for.
Florida Man writes boisterous web tutorial that confounds the livid majestic webosphere!
T___T