New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get unitless value #533

Closed
flops opened this Issue Sep 30, 2012 · 45 comments

Comments

Projects
None yet
@flops

flops commented Sep 30, 2012

Is there any way to parseInt the values and get unitless value ?
All i`ve found that i only can get is it unitless value or not...

For example:

@function clear-units($value){
    @if type-of($value) == "number" {
        @if (unitless($value)) {
            @return $value;
        } @else if unit($value) == "em"{
            @return $value / 1em;
        } @else if unit($value) == "px" {
            @return $value / 1px;
        } @else if unit($value) == "pt" {
            @return $value / 1pt;
        }
    } @else {
        @warn "Not a number value: #{$value}";
        @return $value;
    }
}

made this function. But as you can see all units must be declared in if statement, and i don`t really like the realization.

@pdaoust

This comment has been minimized.

Contributor

pdaoust commented Oct 3, 2012

yes, it does seem a bit verbose. I wrote a function like that myself... It would be nice to see this rolled into SASS core, along with a cast() function that converts one unit into another, without doing arithmetic on it (with the exception of %, which should be divided by 100).

@nex3

This comment has been minimized.

Contributor

nex3 commented Oct 5, 2012

There's a built-in unitless() function that does this.

@nex3 nex3 closed this Oct 5, 2012

@flops

This comment has been minimized.

flops commented Oct 6, 2012

Not even close.
unitless - gives you Bool.
Function in example returns value without units, to convert em to px for example.

@nex3

This comment has been minimized.

Contributor

nex3 commented Oct 12, 2012

Sorry, you're correct. Reopening.

@nex3 nex3 reopened this Oct 12, 2012

@robwierzbowski

This comment has been minimized.

Contributor

robwierzbowski commented Dec 25, 2012

In the meanwhile, if you don't want to cycle through all of those ifs:

@function strip-units($number) {
  @return $number / ($number * 0 + 1);
 }

http://stackoverflow.com/questions/12328259/how-do-you-strip-the-unit-from-any-number-in-sass/12335841#12335841

@pdaoust

This comment has been minimized.

Contributor

pdaoust commented Jan 8, 2013

@robwierzbowski Thanks for the tip; I'll incorporate it into my own stuff and see if it works!

@chriskempson

This comment has been minimized.

chriskempson commented Mar 18, 2013

Would love to see this in SASS core.

@HugoGiraudel

This comment has been minimized.

Contributor

HugoGiraudel commented Jul 17, 2013

Function from @robwierzbowski works great. I'd love to see this native. It occurred to me it comes in handy sometimes.

@chriseppstein

This comment has been minimized.

Member

chriseppstein commented Jul 19, 2013

I'd like to see use cases where this is a good idea. I have doubts.

@chriseppstein

This comment has been minimized.

Member

chriseppstein commented Jul 19, 2013

I think too many people don't know how to work with units and will just use this to do bad things instead of proper math. :(

@HugoGiraudel

This comment has been minimized.

Contributor

HugoGiraudel commented Jul 22, 2013

So... a function returning a unique identifier per Sass run (#771) is okay, but a function returning an integer from a length/percentage is not? :D

What I find interesting is you provide a function to return the unit from a length (unit()), but not a reverse function. It seems kind of odd to me.

@chriseppstein

This comment has been minimized.

Member

chriseppstein commented Jul 22, 2013

@HugoGiraudel I gave my concerns and I asked for use cases. Your most recent comment is unproductive and unpersuasive.

@robwierzbowski

This comment has been minimized.

Contributor

robwierzbowski commented Jul 22, 2013

I used to have a real-life use case for this, but I can't find an example in my recent work. Off the top of my head:

@function raise($measurement, $exponent) {
  $result: $measurement;
  @for $i from 1 through ($exponent - 1) {
    $result: $result * without-unit($measurement);
  }
  @return $result;
}

@ericam is the original stackoverflow answer-er, and might have some in-use examples.

@HugoGiraudel

This comment has been minimized.

Contributor

HugoGiraudel commented Jul 22, 2013

I didn't mean to sound rude or anything Chris. Sorry if I offended in any case. I just thought this isn't a borderline feature.

I remember having used this in a REM/PX mixin a while back (http://hugogiraudel.com/2013/03/18/ultimate-rem-mixin/). Not a key feature, but definitely useful in some cases. :)

@chriseppstein

This comment has been minimized.

Member

chriseppstein commented Jul 22, 2013

@robwierzbowski This actually makes my point. It's nonsense mathematically to perform and exponential operation with a dimensional value. Taking the unit off, makes the operation succeed, but it doesn't make sense. What does 2px ^ 5em mean? By doing this, you're making something succeed that should be an error to the caller.

@HugoGiraudel That article is just another case of people who do not understand how arithmetic with dimensional values works. I want us to focus on training them on how to do the math correctly, not giving tools to enable bad behavior.

@robwierzbowski

This comment has been minimized.

Contributor

robwierzbowski commented Jul 22, 2013

@chriseppstein: Note with the example function there's no way to do 2px ^ 5em. The exponent can only be a unitless number, and the without-unit is only applied when multiplying the measurement to itself. It is correct math.

But, I would like to see some real world, in-use examples. Like I said, at one point I wanted this but haven't needed it for quite a while, and have no opinion on whether it should be part of Sass or not.

@chriseppstein

This comment has been minimized.

Member

chriseppstein commented Jul 22, 2013

@robwierzbowski Oh, sorry I misread that. Again, this is a misunderstanding of how dimensional values work. 2ft ^ 2 is 4 square feet (an area), not 4 ft (which would be a length). The correct thing to do in this calculation is let the units multiply along with the number. the easiest way to think how to properly do math with units is to think of a unit like it is an unknown quantity in algebra. (2 * x) ^ 2 is 4 * x^2. If the units at the end of a calculation don't cancel out to become the dimension you're expecting, this is a good sign that a mathematical error was made.

@mirisuzanne

This comment has been minimized.

mirisuzanne commented Jul 22, 2013

I don't have any use cases for stripping units. I just enjoy solving the puzzle. I have 3-4 more useless Sass functions if anyone wants them.

I think we did try to manipulate units early on with Susy, and quickly realized - after a scolding from Chris :) - that it was much simpler and more reliable to keep the units in place. I agree with @chriseppstein that there is really no good reason for this, at least not one I've ever thought of.

@robwierzbowski

This comment has been minimized.

Contributor

robwierzbowski commented Jul 22, 2013

@chriseppstein Thanks, that makes a lot of sense.

@HugoGiraudel

This comment has been minimized.

Contributor

HugoGiraudel commented Jul 22, 2013

I have 3-4 more useless Sass functions if anyone wants them.

I do. :)

@cimmanon

This comment has been minimized.

cimmanon commented Jul 22, 2013

The only reason I strip the units off of a number is because I wish to use em as my fallback for rem, rather than px. I could just forget about rem since I've correctly calculated what the em equivalent is, but there may be rounding errors, depending on the browser involved.

http://codepen.io/cimmanon/pen/KfnuA

body {
  font-size: 133%;
}

.em {
  border: 0.75187em solid;
}

.rem {
  border: 1rem solid;
}

In Opera 12.16 (until Opera 15 has the same features as v12, this browser may as well not exist), for instance, the .em element border has a calculated width of 15px, while the .rem element border has a calculated width of 16px.

@robwierzbowski

This comment has been minimized.

Contributor

robwierzbowski commented Jul 22, 2013

I think @chriseppstein's point is that you/we should be using Sass math to convert between units because it is idiomatic and less prone to accidental failure. I'm sure you can convert rem to em in your mixin without stripping the unit.

@pdaoust

This comment has been minimized.

Contributor

pdaoust commented Jul 23, 2013

I use a little 'unitless' function all the time. My primary (actually, only) use case is for conversion between units, which I believe Sass doesn't have built in (although I know Compass has it built in). I'm in a rather unpleasant tooling environment (Visual Studio), so I can't expect Compass to reliably work, even with Web Workbench. That's why I'm doing unit conversion right in SCSS. For instance:

// Hats off to @robwierzbowski for this; it's a lot simpler than my original function
@function strip-units($value) {
    @return $value / ($value * 0 + 1);
}

@function convert-angle($value, $unit) {
    $old-units: unit($value);
    $unitless-value: strip-units($value);

    $base-value: null;
    // Convert the angle to degrees, but without the degree unit
    @if $old-units == grad {
        $base-value: $unitless-value * 0.9;
    } @else if $old-units == turn {
        $base-value: $unitless-value * 360;
    } @else if $old-units == rad {
        $base-value: $unitless-value * (180 / 3.1415926);
    }

    // Now convert it to the desired units
    @if $unit == deg {
        @return $base-value * 1em;
    } @else if $unit == grad {
        @return $base-value / 0.9 * 1grad;
    } @else if $unit == turn {
        @return $base-value / 360 * 1turn;
    } @else if $unit == rad {
        @return $base-value * (3.1415926 / 180) * 1rad;
    }
}

Now I realise this doesn't constitute an ideal use case -- I'm very happy to just have my strip-units() function, and alternatively I suppose I could get more serious with SASS math, like so:

@if $old-units == deg {
    $base-value: $value / 1deg;
} @else if $old-units == grad {
    $base-value: $value * 0.9 / 1grad;
} @else if $old-units == turn {
    $base-value: $value * 360 / 1turn;
} @else if $old-units == rad {
    $base-value: $value * 180 / 3.1415926rad;
}

(Is this an idiomatic use of SASS math? It works, but is it muddying the concept at all? For some reason it doesn't improve the meaning of the code for me, even though I do understand the basics of dimensional math. It seems like I'm just stripping the unit anyway, one way or another.)

Anyhow, after having weighed in with my support, I really don't think I prefer either way over the other. So just ignore my use case if you don't think it adds any value :)

@chriseppstein

This comment has been minimized.

Member

chriseppstein commented Jul 23, 2013

@pdaoust I'd do it like this:

$convertable-units: deg grad turn rad;
$conversion-factors: 1 10grad/9deg 1turn/360deg 3.1415926rad/180deg;
@function convert-angle($value, $unit) {
  @if index($convertable-units, unit($value)) and index($convertable-units, $unit) {
    @return $value
             / nth($conversion-factors, index($convertable-units, unit($value)))
             * nth($conversion-factors, index($convertable-units, $unit));
  } @else {
    @warn "Cannot convert #{unit($value)} to #{$unit}";
  }
}
@chriseppstein

This comment has been minimized.

Member

chriseppstein commented Jul 23, 2013

I'm going to close this. I've yet to see a single good use case and so I remain convinced this is an anti-pattern.

@robwierzbowski

This comment has been minimized.

Contributor

robwierzbowski commented Jul 23, 2013

Convinced. 👍

@HugoGiraudel

This comment has been minimized.

Contributor

HugoGiraudel commented Jul 23, 2013

Nice function Chris. I would have probably used a single 2-dimensional lists instead of 2 lists but I guess it's a matter of taste here. Thanks for the useful one anyway. ;)

@chriseppstein

This comment has been minimized.

Member

chriseppstein commented Jul 23, 2013

@HugoGiraudel ya. that works too. This was easier to write on the fly :)

@pdaoust

This comment has been minimized.

Contributor

pdaoust commented Jul 26, 2013

you know, actually, I was in the process of reimplementing my conversion functions as two-dimensional lists :) Thanks, @chriseppstein, for the example converter -- once I parsed it, I understood the math that was going on.

@chriseppstein

This comment has been minimized.

Member

chriseppstein commented Jul 26, 2013

I think Sass's math is really nice, once the lightbulb goes on!

@montmanu

This comment has been minimized.

montmanu commented Feb 28, 2014

Not trying to get involved in the back and forth, just wanted to share one particular use case I recently came across that led me here:

$screen-sm-min: 768px;
$thumbnail-width: 150px;
$thumbnail-height: 150px;
$thumbnail-width-sm: 300px;
$thumbnail-height-sm: 300px;

.thumbnail {
  width: $thumbnail-width;
  height: $thumbnail-height;
  background-image: url("http://placehold.it/#{$thumbnail-width}x#{$thumbnail-height}");
}

@media (min-width: $screen-sm-min){
  .thumbnail {
    width: $thumbnail-width-sm;
    height: $thumbnail-height-sm;
    background-image: url("http://placehold.it/#{$thumbnail-width-sm}x#{$thumbnail-height-sm}");
  }
}

The URL expects a unitless integer, so without any changes, the above compiles to something like:

.thumbnail {
  background-image: url("http://placehold.it/300pxx300px");
}

when what is desired is:

.thumbnail {
  background-image: url("http://placehold.it/300x300");
}

Obviously, there are several solutions to achieve the desired effect, but what I was looking for was something similar to the following:

.thumbnail {
  background-image: url("http://placehold.it/#{parse-int($thumbnail-width)}x#{parse-int($thumbnail-height})");
}
@pdaoust

This comment has been minimized.

Contributor

pdaoust commented Feb 28, 2014

@montmanu That's a very unusual, but very interesting and valid, use case. I can understand the core team's reluctance to make this a built-in function, but @robwierzbowski 's snippet is perfectly adequate. You could just call it parse-int() or whatever you like:

@function strip-units($value) {
    @return $value / ($value * 0 + 1);
}
@montmanu

This comment has been minimized.

montmanu commented Feb 28, 2014

@pdaoust So do I.. I was reluctant to even share that example knowing that this issue has generated a bit of disagreement and is now closed, so I wasn't sure what value it would add to the discussion. @robwierzbowski's solution is what I ended up using and it has met my needs. Maybe worth mentioning that when naming the function parse-int() the grunt-contrib-compass task I was using had problems compiling. I didn't do too much digging to find out why, but when I renamed it to something less likely to create a namespace conflict (e.g., strip-units()) it compiled successfully.

@chriseppstein

This comment has been minimized.

Member

chriseppstein commented Feb 28, 2014

@montmanu This code is actually shorter and it works:

.thumbnail {
  background-image: url("http://placehold.it/#{$thumbnail-width / 1px}x#{$thumbnail-height / 1px)");
}
@cimmanon

This comment has been minimized.

cimmanon commented Feb 28, 2014

@chriseppstein Yes, it is shorter, but to someone who isn't familiar with what's going on here, 1px looks like some sort of magic number. Using a clearly-named function removes this ambiguity.

@ucavus

This comment has been minimized.

ucavus commented Apr 4, 2014

I have a valid use case: unitless line height. I wanted to make a line height from a ratio of two variables that have units. Yes this still produces a valid line height rule, but it completely ruins natural CSS inheritance. Unitless line heights are very useful because they're interpreted as proportional to the font size of the context. (I usually don't use anything else.) With a unit, it becomes a fixed size everywhere, resulting in text that overlaps or is too sparse.

@HugoGiraudel

This comment has been minimized.

Contributor

HugoGiraudel commented Apr 4, 2014

Divide one value by the other? For instance, 1.5em / 1em.

@ucavus

This comment has been minimized.

ucavus commented Apr 4, 2014

I was just coming back to write that I'd made a mistake. :P

@MattyBalaam

This comment has been minimized.

MattyBalaam commented Apr 16, 2014

Possibly there is a better solution to this, but I have a case at the moment which I can only think to solve with a unitless result.

I have an image sprite which needs a defined size to crop, so I am then scaling the image to fit into other spaces with a transform.

I would like to work out this scale automatically can do this by dividng the image sprite size with the size of other elements. e.g. in one case the image sprite is 353px wide but I want it to fit into an area with a width of 115px - dividing 115/353 seems to give me the correct ratio to accomplish this.

@chriseppstein

This comment has been minimized.

Member

chriseppstein commented Apr 16, 2014

115px/353px results in the same unitless ratio.

@MattyBalaam

This comment has been minimized.

MattyBalaam commented Apr 16, 2014

You are right. I think I had assumed I would get a px result from that calculation, sorry for the noise.

@drewlustro

This comment has been minimized.

drewlustro commented May 6, 2014

Sick strip-units() function @robwierzbowski

👍

@robwierzbowski

This comment has been minimized.

Contributor

robwierzbowski commented May 6, 2014

Thanks, not my own! Copied/adapted from another source far in the past.

Honestly I haven't had to strip units since this issue was posted.
@chriseppstien ++

On Tuesday, May 6, 2014, Drew Lustro notifications@github.com wrote:

Sick strip-units() function @robwierzbowskihttps://github.com/robwierzbowski

[image: 👍]


Reply to this email directly or view it on GitHubhttps://github.com//issues/533#issuecomment-42348889
.

Rob Wierzbowski
@robwierzbowski http://twitter.com/#!/robwierzbowski
http://github.com/robwierzbowski
http://robwierzbowski.com

@marknotton

This comment has been minimized.

marknotton commented Aug 18, 2014

Not really necessary anymore, but I took this approach to the same task.

// Remove units from a number.
@function strip-units($number) {
  @if type-of(#{$number}) == "number" or not unitless($number) {
    $num-length  : str-length(#{$number});
    $unit-length : str-length(unit($number));
    @return str-slice(#{$number}, 1,  $num-length - $unit-length);
  } @else {
    @warn "This isn't a number, or it doesn't have any units anyway.";
    @return $number;
  }
}
@chriseppstein

This comment has been minimized.

Member

chriseppstein commented Aug 18, 2014

@marknotton That function returns a string not a number. Please. Just use unit based arithmetic and you'll never need this function.

@sass sass locked and limited conversation to collaborators Aug 18, 2014

whatsnewsisyphus added a commit to whatsnewsisyphus/knife that referenced this issue Nov 8, 2014

Streamlined calculateRem / krem, incorporating stripUnits to accept n…
…on px size declarations

included a stripUnits function as suggested by https://github.com/ronilaukkarinen on Pushplaybang#3. This is originally an excerpt from sass/sass#533 by https://github.com/robwierzbowski

The suggestion on Pushplaybang#3 broke the ie/px fallback, printing unitless units like the following, so I plugged in stripUnits there as well, and added the px suffix back in
```
font-size:16;
font-size:1rem
```

Streamlined calculateRem while I was at it
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.