Everything.

A blog about nothing. By Don Kuntz.

Simple Parallax

I made some updates to my profile page a couple days ago, mostly adding in a new cover photo-esque thing, like on most social networks. I’m also a huge sucker for images that parallax behind content, so I wrote some javascript to give my cover image the same effect.

When I say simple, I mean fairly simple, the whole file is 38 lines with a lot of space, and doesn’t use any libraries, just vanilla javascript (because the whole site is just the one page, I didn’t want to have to include a library to get a simple parallax effect working).

How it works at the most basic form is by having three things in the DOM: a cover div, which contains the cover image, and is given an arbitrary height (I gave it 700 pixels initially, getting smaller if the viewport is smaller); the actual image (which is absolutely positioned), inside of the cover div; and the contents div.

The basic HTML page’s body looks like this

``` html


You probably shouldn't leave the style parts in the HTML semantically, but it's
there to make sure you see the cover container has a set height and the image is
absolutely positioned.

That's all you really need HTML and CSS wise, it might be a good to set the
body's padding to 0, but that's a personal choice.

As for the JavaScript, there's only one function required to make the parallax
effect work, but I wrote another to make sure everything stays in bounds.

The actual scroll function looks like this

```javascript
function scroll() {
    // these first two variables aren't totally necessary, but
    // they make the actual fun easier

    // holds the number of pixels from the top of the page
    // that the viewport is currently at (as in, if you scroll
    // down ten pixels from the top, this value is 10)
    var offset = window.pageYOffset

    // hold a reference to the cover image's DOM element
    var cover = document.getElementById('cover-img')


    // set the top margin of the cover image to the offset divided
    // by some value. I use 2, which means that for every two pixels
    // the main content moves, the image moves one
    cover.style.marginTop = (offset / 2) + "px"
}

// tell the window to call `scroll()` when a scroll event occurs
window.onscroll = scroll

That’s all you need for a very basic parallax effect, the only thing you can really change is the offset / 2. You can make it anything you want, something smaller means the change is less visible, something larger means the change is more visible (and might cause the top of your image to be lower than the top of the window’s viewport.

However, I wanted a slightly more complex script, which made sure everything stayed in bounds (especially if the image’s height is less than the cover div’s height). That function looks like this

// hold an initial offset for the image (extra margin-top pixels)
var initOffset = 0

function setupStuffs() {
    // set references for the cover image and cover div
    var coverImg = document.getElementById('cover-img')
    var coverDiv = document.getElementById('cover')

    // if the actual viewport is smaller than the cover div's height,
    // set the cover div's height to the viewport's height.
    //
    // The cover div's height probably shouldn't be hardcoded in the
    // JS, but is...
    if (window.innerHeight < 700)
      coverDiv.style.height = window.innerHeight + "px"

    // If the cover image's height is smaller than the cover div's
    // height, change that, too
    if (coverImg.offsetHeight < coverDiv.offsetHeight)
      coverDiv.style.height = coverImg.offsetHeight + "px"


    // if the image is larger than the viewport, we'll have problems
    // when we get to the bottom of the page (the content'll finish,
    // but there'll be more image). To fix this, we have an initOffset
    // variable (initialized outside of the function to be global, because
    // it'll be used in scroll, too). initOffset holds a number of extra
    // pixels to push the image up (giving it a negative margin-top to
    // start)
    if (coverImg.offsetHeight > window.innerHeight)
      initOffset = coverImg.offsetHeight - window.innerHeight
    else
      initOffset = 0


    // call scroll() to make sure our changes take effect
    scroll()
}

// set the window's load and resize events to call setupStuffs()
window.onresize = setupStuffs
window.onload = setupStuffs

Putting that all together (minus the comments), you should get something that looks like like this:

var initOffset = 0

window.onload = setupStuffs
window.onresize = setupStuffs
window.onscroll = scroll

function scroll() {
    cover.style.marginTop = (offset / 2) - initOffset + "px"
}

function setupStuffs() {
    var coverImg = document.getElementById('cover-img')
    var coverDiv = document.getElementById('cover')

    if (window.innerHeight < 700)
        coverDiv.style.height = window.innerHeight + "px"

    if (coverImg.offsetHeight < coverDiv.offsetHeight)
      coverDiv.style.height = coverImg.offsetHeight + "px"


    if (coverImg.offsetHeight > window.innerHeight)
        initOffset = coverImg.offsetHeight - window.innerHeight
    else
        initOffset = 0

    scroll()
}

And there you have it, a simple, single image parallax effect.