Search code examples
csscss-transitionspercentagebackground-position

Background-position negative percentage values


I want to give my header a background-image when a class gets changed, and smoothly animate in the background. Because the element (in my case - not in the example below) has a variable height, I want to use percentages to offset the background-position outside the element so it can smoothly scroll in once that class changes. The following code, however, stumped me a bit:

var test = document.getElementById("test");
var span = document.getElementById("change");

setInterval(function(){
  test.className = test.className.indexOf("test") >= 0
    ? test.className.replace("test", "").replace("  ", " ").trim()
    : (test.className + " test").trim();
},3000);

test.addEventListener("click", function(event){
  var testing = test.className.indexOf("in-pixels") >= 0;
  span.innerHTML = testing ? "pixels" : "percentages";
  test.className = testing
    ? test.className.replace("in-pixels", "").replace("  ", " ").trim()
    : (test.className + " in-pixels").trim();
});
#test {
    border: 1px solid #000;
    cursor: pointer;
	width: 200px;
	height: 200px;
	background-image: url(http://placehold.it/200x200);
	background-size: auto 100%;
	background-repeat: repeat-x;
	background-position: 0% -100%;
	-webkit-transition: background-position 500ms;
	   -moz-transition: background-position 500ms;
		-ms-transition: background-position 500ms;
			transition: background-position 500ms;
}
#test.test {
	background-position: 0% 0%;
}
#test.in-pixels {
	background-position: 0% -200px;
}
#test.in-pixels.test {
	background-position: 0% 0;
}
<div id="test"></div>
<p>Click above to change to <span id="change">pixels</span></p>

The above is an eternal loop that will activate the testing class. Clicking on the box will change the set CSS value from percentage to pixels and back, illustrating the issue as the animations starts back up when pixels get applied, but dies down once you take that off again.

Why does setting the background-position to a negative percentage value not work here [correction: neither negative nor positive percentage values work], but setting it to a pixel value does work? According to the documentation (MDN), background-position can have negative % values, and it works in pixels, so what going on here?

I am using Safari 8.0.2 and also tested this on Chrome 41.0.2272.118 (64-bit), Mac


Solution

  • The issue is that the percentage value is not what we (or at least I) thought it was. Or at least it is not calculated using the assumed values.

    Is the percentage calculated from the width/height of the containing box? The answer is "No".

    According to the W3 documentation:

    Percentages refer to size of background positioning area minus size of background image; see text

    And from the MDN link that you shared:

    Percentages refer to the size of the background positioning area minus size of background image; size refers to the width for horizontal offsets and to the height for vertical offsets


    What does this mean?

    The background position percentage is calculated relative to the resulting number of:

    box size - image size = [number used for the percentage calculations]
    

    In your particular case, the background image is 200 pixels by 200 pixels, and the box has the same size. Then, the percentage is not referred to the 200 pixels of the box but to:

    200 - 200 = 0
    

    As the result is 0, whatever number/percentage you multiply by it will result in 0.