- Common Loki Misconfigurations
- Iterating Through a List in Ink
- Debugging Misconfigured Container Networks
- Minimum Viable EC2 in Terraform
- Storylets in Ink
- Interactive Fiction Tooling Overview
- In-Place Resizing for Digitalocean Droplets
- Unity Demonstrates the Importance of FOSS
- Target Labels in Prometheus
- My View of AI is the Same
- Verify DNS Ownership with TXT Records
- Sane Droplet Defaults
- Editing Made Easy with Vim
- Gatsby Gotchas
- Concatinating Default AWS Tags in Terraform
- Easily Updating the Default Github Branch
- Lifetimes in Rust
- Checking for Bad Links
- Maybe TypeScript and React is Bad
- Static Asset Management in React
- Bundler Down Time
- Using React Context for Localization
- JS Implementation of a Sticky Footer
- Custom Aliases
- Trying Out the 7drl Challenge
- Trash Opinions
- Building Your First Program in Rust
- Fixing mongod reports errors related to opening a socket
- Improving Open Source Maintenance
- Technical Interviewing Tips
- Housekeeping Note
- Dynamic Programming Basics
- The Oddity of Naming Conventions in Programming Languages
- An Experiment Using Machine Learning, Part 3
- Debugging with grep
- An Experiment Using Machine Learning, Part 2
- An Experiment Using Machine Learning, Part 1
- The Value of while
- National Day of Civic Hacking
- OpenAI and the Future of Humanity
- Creating a Whiteboard App in Django
- Creating Meaningful, Organized Information
- Towards A Critique of Social Media Feeds
- Setting up Routes in Django
- Developing a Messaging Component for Code for SF
- Dream Stream 2.0
- Keyed Collections in Javascript: Maps and Sets
- Blog Soft Relaunch
- Scraping with Puppeteer
- Looking Ahead to Dream Stream 2.0
- Solving West of Loathing's Soupstock Lode Puzzle
- Installing Ubuntu
- Interview with David Jickling Evaluation
- Compare Text Evaluation
- Dream Stream Evaluation
JS Implementation of a Sticky Footer
Suppose we want a footer at the bottom of a page. It’s a common enough requirement for most web pages. You typically might include various legal documents, and other information that a regular user isn’t normally going to consult, and therefore doesn’t belong in the navbar, but is still important to provide. Our footer component will have the following rules in CSS:
.footer {
position: absolute;
bottom: 0;
}
All of the content in .footer
now lives at the bottom of our browser window. This is simple, but creates a new problem. What if we have more than a window’s worth of content on the screen? Our footer will now occupy the same space as some of the content. Bad! What we really want is a sticky footer. That is, a footer that will stick to the bottom of the page, unless there is a page or more worth of content, in which case it will behave normally.
Chris Coyer goes through a history of CSS solutions for this problem. If I were starting a project from scratch, I would probably use the flexbox solution. It’s nice and simple, and it uses CSS that should be supported by nearly every browser. For reference, here’s the code:
html, body {
height: 100%;
}
body {
display: flex;
flex-direction: column;
}
.content {
flex: 1 0 auto;
}
.footer {
flex-shrink: 0;
}
However what happens when you need to implement this component in an existing codebase with some other way of defining the layout? If you’re like me, you are terrified of the idea of going around and messing with “global” variables like html
and body
. Who knows what sort of side effects that might have? The odds of going down crazy rabbit holes trying to fix unanticipated behavior sound high and not fun.
When we don’t have a great way to handle problems with pure CSS, we can always use JavaScript. I was dealing with this problem for a site that uses React and TypeScript. The footer component had the following code:
public componentDidMount() {
this.setState({
anchored: this.absolutelyPosition,
});
}
...
private get absolutelyPosition() {
const bodyHeight = document.body.clientHeight;
const viewportHeight = window.innerHeight;
return bodyHeight < viewportHeight;
}
So we have the absolutelyPosition
method that calculates the document and viewport height, and returns a boolean depending on if the body height is less than the viewport height. The anchored
state is set to this boolean. What does anchored
do? It does this:
const elementClasses = classNames(
this.elementClass,
this.props.className, {
[`${this.elementClass}--anchored`]: this.state.anchored,
}
);
Every component has some associated class names that are determined by props and other factors. In this case we have a rule that says if the anchored
state is true we attach the phrase --anchored
to our class name. So our CSS can now look something like this:
.footer--anchored {
position: absolute;
bottom: 0;
}
So now we have a footer that may or may not have an --anchored
class name attached to it depending on the viewport height and the height of the content for the page. In cases where the amount of content of the body of the page is less than the viewport height it has absolute positioning and sticks to the bottom of the page. But in instances where that isn’t true it just behaves as a normal element.
So we’re done, right? Not quite. The problem I had with this code is it makes the assumption that our body height is a fixed size. But mobile displays can be turned, and in a desktop environment users might be resizing the their browser window for reasons. In that event the user is in for a nasty surprise with an out of flow footer.
So we need to add an event listener to pay attention to when the resize happens.
public componentDidMount() {
window.addEventListener('resize', this.handleResize);
this.setState({
anchored: this.absolutelyPosition,
});
}
private handleResize = () => {
let bodyHeight = document.body.clientHeight;
let viewportHeight = window.innerHeight;
if (bodyHeight < viewportHeight) {
this.setState({
anchored: true,
});
} else {
this.setState({
anchored: false,
});
}
}
Now we have an event handler, handleResize
that fires off anytime a window resize happens. It recalculates the body and viewport height and sets state accordingly. Our footer now behaves properly, and doesn’t get in the way of page content, while sticking to the bottom when there is room for it.