3

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

somethinghere
  • 16,311
  • 2
  • 28
  • 42
  • 1
    I think it is important to notice that the fact that the percentage is negative is not the cause of the issue. If you change it to a positive percentage (200% instead of -100%, and 200px instead of -200px), it will fail in the same way: http://jsfiddle.net/caz5jxz7/ It is an issue with percentages (in general), and not with negative percentages – Alvaro Montoro Apr 16 '15 at 13:15
  • @AlvaroMontoro thanks for that, that's true. I added an [Updated:] to my question to clarify it! – somethinghere Apr 16 '15 at 13:33
  • If you want to use percentages you could use :before. See my js fiddle: - http://jsfiddle.net/ben1/hd5sv7uL/1/ – Ben Apr 16 '15 at 14:34
  • @Ben Damn thats true! I use them all the time but I couldn't think of them this time... Thanks, thats the non-DOM heavy solution I was looking for (and yes, I know its technically still just as much overhead as an actual DOM element but it keeps HTML clean and style as exclusive to CSS as possible!) Cheers Mate – somethinghere Apr 16 '15 at 15:42
  • If you are still intrested you can find a full explanation in the duplicate and 2 workaround at the bottom to do what you want where one doesn't involve extra DOM element or pseudo element – Temani Afif Jan 10 '20 at 00:13

1 Answers1

3

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.

Alvaro Montoro
  • 28,081
  • 7
  • 57
  • 86
  • Hmmm... This is right, thanks for that! I guess I'll have to use a amount of pixels. Cheers! – somethinghere Apr 16 '15 at 13:40
  • @somethinghere You could still use percentages by "faking it": wrap the `#test` box inside another box (e.g.: `#parenttest`), then make the parent have the size that you want, and make `#test` have double that size with a background of 50% (to match the parent)... That sounds complicated, so here is an example that will help see it better: http://jsfiddle.net/caz5jxz7/3/ – Alvaro Montoro Apr 16 '15 at 13:49
  • I know there are other ways to deal with this, I can use images that are hidden, fake boxes etc.. I just thought that the background positioning would be an elegant non-DOM heavy solution (I try to avoid adding elements for the sake of design). Thanks for the suggestion, though! I decided to tool around a bit to get it working with pixel values in a controlled fashion. Cheers! – somethinghere Apr 16 '15 at 14:04