This was an experiment to test two technologies. The first was CSS Grid, which was conceived by Phil Cupp at Microsoft in 2011 for content layout —which later incorporated the dense-pack layout attribute. This would allow items of disparate dimensions to fit together automatically just by adding the keyword 'dense'. (Formerly this ‘dry-masonry’ approach would take many lines of JavaScript. ) The second technology was the implementation of the Fischer-Yeats Shuffle, an algorithm to randomize an array of items. (see https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle.)
So after a lot of research and fiddling, I loaded a database table with columns ID, Attribution, Text, Title, Type
with as many groaners as I could find. Then wrote ColdFusion code to read all the data and create a randomly shuffled array of all 158 records as JSON objects:
<cfscript>
// Query database
q = new Query(sql="SELECT ROW_NUMBER() OVER(ORDER BY ID ASC) AS ID, Title,
Text, Attribution, Type FROM Jokes",
datasource="training").execute().getResult();
// set variables
c = q.recordCount;
m = q.recordCount;
arJokes = [];
i = 0;
t = 0;
// Create an array of row numbers
arRows = valueArray(q, "ID");
// Randomize the array of row numbers using the Fisher-Yates shuffle https://bost.ocks.org/mike/shuffle/
while(m) {
i = max(int(rand("SHA1PRNG") * (m+1)), 1);
t = arRows[m];
arRows[m] = arRows[i];
m--;
}
// loop through array of row numbers, building the objects
// 250 words per minute = 4.5 words per second to read a joke
for( i=1; i <= c; i++ ) {
arJokes[i] = {};
arJokes[i]['jokeID'] = q.id[arRows[i]];
arJokes[i]['jokeTitle'] = q.title[arRows[i]];
arJokes[i]['jokeText'] = q.text[arRows[i]];
arJokes[i]['jokeAttribution'] = q.attribution[arRows[i]];
arJokes[i]['jokeType'] = q.type[arRows[i]];
// Total number of words
arJokes[i]['jokeLength'] = max(round( (listLen(q.Text[arRows[i]], ' ') +
listLen(q.Title[arRows[i]], ' ') +
listLen(q.Attribution[arRows[i]], ' '))/4.5) * 1000, 6000);
// Randomized CSS class name
arJokes[i]['bgClass'] = 'bg' & ( i % 8 > 0 ? i %8 : randRange(1,7,"SHA1PRNG") );
}
json = serializeJSON(arJokes);
</cfscript>
Each element of the randomized array now looks like this:
{ bgClass: "bg4", jokeAttribution: "http://pun.me/pages/dad-jokes.php", jokeID: 151, jokeLength: 6000,jokeText: "Two goldfish are in a tank. One says to the other,“do you know how to drive this thing?”"
jokeTitle: "Slosh fund", jokeType: "Dad Joke"
}
...where the CSS class is also randomly selected based on the length of the joke. The rest of the values are what is in the database. The jokeLength
was the number of milliseconds required to read the joke and groan. Experimentally, this averaged out to about 5 seconds, so I ended up using a simple interval of 5 seconds.
Then JavaScript Fetches the shuffled array of objects, and steps through it, adding a joke to the container div using the requisite CSS class in the template.
<script>
var intervalID = window.setInterval(renderJoke, 5000);
function renderJoke() {
if( i < arJokes.length ) {
jokeTextLength = arJokes[i].jokeText.length;
if(jokeTextLength <= 100) {
arJokes[i].divClass = '';
} else if(jokeTextLength > 100 && jokeTextLength <= 150) {
arJokes[i].divClass = ' horizontal';
} else if(jokeTextLength > 150 && jokeTextLength <= 300) {
arJokes[i].divClass = ' vertical';
} else {
arJokes[i].divClass = ' big';
}
}
joke = arJokes[i];
let jokeTpl =
`<div class="${joke.bgClass}${joke.divClass}">
<header>${joke.jokeTitle}</header>
<p>${joke.jokeText}</p>
<footer>~ ${joke.jokeAttribution}</footer></div>`
if( i > 0 ) {
container.insertAdjacentHTML( 'beforeend', jokeTpl );
} else {
container.innerHTML = jokeTpl;
}
} else {
window.clearInterval(intervalID);
}
i++;
}
</script>
The CSS Grid dense-pack algorithm fits in each joke, irrespective of size, to fill available space in the container div.
.container {
display: grid;
grid-gap: 5px;
grid-template-columns: repeat(auto-fit, minmax(330px, 1fr));
grid-auto-rows: 160px;
grid-auto-flow: "dense";
width: 92vw;
margin: 10px auto;
background: white;
border: solid 5px white;
box-shadow: 5px 5px 5px rgba(0,0,0,.5);
}
It took me a lot of time, and trial and error to get it to work satisfactorily. (Recently as a test, I challenged Claude.ai to write the whole thing. It took him three seconds. I immediately hired him as an assistant!)