rodneyrehm / viewport-units-buggyfill

Making viewport units (vh|vw|vmin|vmax) work properly in Mobile Safari.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

!important styles don't get overriden

mderazon opened this issue · comments

I have a React.js component that is setting the height of the component as an inline style and I have no control over it.
To make it work with buggyfill, I had to create a class with !important to override the precedency of the inline styles of the lib. Therefor I have something like this in my CSS file:

.map {
  height: 85vh !important;
}

When debugging on IOS7, I see the fixed px class that is the result of buggyfill, but the above class definition still takes precedency over the 3 (the buggyfill version, the inline version and the !important class version)

Any ideas how to bypass this ?

found a workaround.
changed line 326
from

return (_number * _base) + 'px';

to

return (_number * _base) + 'px !important';

You could try a bit more generic approach to solving the issue in replaceValues():

function replaceValues(match, number, unit, source) {
  var _base = dimensions[unit];
  var _number = parseFloat(number) / 100;
  var _important = source.indexOf('!important') !== -1 ? ' !important' : '';
  return (_number * _base) + 'px' + _important;
}

let me know if that works for you (I haven't tested it) so we may improve the buggyfill instead of dealing with custom workarounds…

You've changed replaceValues signature from match, number, unit to match, number, unit, source without changing the caller function ?
Am I missing something ?

It doesn't work, tells me that variable source is undefined

The caller is String.prototype.replace() and it knows the signature of the supplied callback. I was missing the offset parameter, though:

function replaceValues(match, number, unit, offset, source) {
  var _base = dimensions[unit];
  var _number = parseFloat(number) / 100;
  var _important = source.indexOf('!important') !== -1 ? ' !important' : '';
  return (_number * _base) + 'px' + _important;
}

that did fix the "source undefined" issue but for some reason !important is not being passed to the source.

For example, in my stylesheet I define

.map {
    height: 75vh !important;
}

and printing source variable in replaceValues() I see

75vh

instead of

75vh !important

Then we should investigate how to get that !important accessed…

On line 232 you do

forEach.call(rule.style, function(name) {
  var value = rule.style.getPropertyValue(name);
  viewportUnitExpression.lastIndex = 0;
  if (viewportUnitExpression.test(value)) {
    declarations.push([rule, name, value]);
    options.hacks && options.hacks.findDeclarations(declarations, rule, name, value);
  }
});

CSSStyleDeclaration.getPropertyValue() only gets the property value,

We can use it with CSSStyleDeclaration.getPropertyPriority() (see here)

So it will be something like

forEach.call(rule.style, function(name) {
  var priority = '';
  if (rule.style.getPropertyPriority(name)) {
    priority = ' !important';
  }
  var value = rule.style.getPropertyValue(name) + priority;
  viewportUnitExpression.lastIndex = 0;
  if (viewportUnitExpression.test(value)) {
    declarations.push([rule, name, value]);
    options.hacks && options.hacks.findDeclarations(declarations, rule, name, value);
  }
});

What do you think ?

tested it with my use case, and it looks like it's fine.

Tried using both

.map {
  height: 85vh !important;
}

and

.map {
  height: 85vh;
}

first one converted to 360px !important and the second one to 360px on my iphone 4 so it looks like it's working.

Also, it's safe to assume that getPropertyPriority() returns either "important" or empty string ""

Are there any other implications you're aware of ?

Good Work!

I think pushing the boolean returned by rule.style.getPropertyPriority(name) onto the declarations list declarations.push([rule, name, value, priority]); is the cleaner (as in more obvious) solution, but one that requires a lot more code to be changed, whereas your propsal is limited to a single function. So I'd go with your proposal, rewritten to a 4-line change:

forEach.call(rule.style, function(name) {
  var value = rule.style.getPropertyValue(name);
  // preserve those !important rules
  if (rule.style.getPropertyPriority(name)) {
    value += ' !important';
  }

  viewportUnitExpression.lastIndex = 0;
  if (viewportUnitExpression.test(value)) {
    declarations.push([rule, name, value]);
    options.hacks && options.hacks.findDeclarations(declarations, rule, name, value);
  }
});

Do you want to send a PR for this? (If so, please also add and example to the test.css, which sort of functions as a manual test)