Как найти максимальное число в html

JavaScript Math

Определение и применение

JavaScript метод max() объекта Math позволяет найти и возвратить наибольшее из переданных чисел.

Обращаю Ваше внимание на то, что если хотя бы один из переданных параметров не может быть преобразован в число, то результатом вызова метода будет значение NaN.

Если метод max() вызывается без параметров, то в качестве возвращаемого значения возвращается значение -Infinity.

Поддержка браузерами

JavaScript синтаксис:

Math.max( value1, value2, ...valueX ); 

value - Number

Версия JavaScript

1.0 (ECMAScript 1st Edition)

Значения параметров

Параметр Описание
value1, value2, …valueX Числовые значения.

Пример использования

Базовое использование

const a = 5,
      b = -12,
      c = 20;

// находим минимальное число из переданных значений
Math.min(a, b, c); // возвращаемое значение 5

// находим максимальное число из переданных значений
Math.max(a, b, c); // возвращаемое значение 20

Обрезание значения

В следующем примере мы рассмотрим как с помощью методов min() и max() объекта Math обрезать значение по верхней или нижней границе, если оно превышает или меньше определенного значения:

const a = someFunc(num);

// условие без использования метода min
if (a > boundary) {
  a = boundary;
}

// перепишем условие с помощью метода min
const val = Math.min(a, boundary); // если значение параметра a будет больше значения парметра boundary, то будет выбрано значение параметра boundary

const b = someFunc(num);

// условие без использования метода max
if (b < boundary) {
  b = boundary;
}

// перепишем условие с помощью метода max
const b = Math.max(b, boundary); // если значение параметра b будет меньше значения парметра boundary, то будет выбрано значение параметра boundary

Минимальное и максимальное значение в массиве

В следующем примере мы рассмотрим как с помощью методов min() и max() объекта Math найти минимальное и максимальное значение внутри массива.

// инициализируем переменную, содержащую массив
const arr = [-10, 22, 5, -2];

// находим минимальное число в массиве
Math.min.apply( null, arr ); // возвращаемое значение -10

// находим максимальное число в массиве
Math.max.apply( null, arr ); // возвращаемое значение 22

// тоже самое с помощью спред оператора ES2015

Math.min(...arr); // возвращаемое значение -10
Math.max(...arr); // возвращаемое значение 22

Обратите внимание, что в первом случае был использован метод apply() объекта Function.

JavaScript Math

I have a simple JavaScript Array object containing a few numbers.

[267, 306, 108]

Is there a function that would find the largest number in this array?

Shog9's user avatar

Shog9

156k34 gold badges229 silver badges234 bronze badges

asked Sep 4, 2009 at 14:17

dotty's user avatar

1

You can use the apply function, to call Math.max:

var array = [267, 306, 108];
var largest = Math.max.apply(Math, array); // 306

How does it work?

The apply function is used to call another function, with a given context and arguments, provided as an array. The min and max functions can take an arbitrary number of input arguments: Math.max(val1, val2, …, valN)

So if we call:

Math.min.apply(Math, [1, 2, 3, 4]);

The apply function will execute:

Math.min(1, 2, 3, 4);

Note that the first parameter, the context, is not important for these functions since they are static. They will work regardless of what is passed as the context.

Peter Mortensen's user avatar

answered Sep 4, 2009 at 14:19

Christian C. Salvadó's user avatar

3

The easiest syntax, with the new spread operator:

var arr = [1, 2, 3];
var max = Math.max(...arr);

Source : Mozilla MDN

answered Nov 3, 2015 at 12:16

A.I's user avatar

A.IA.I

1,4382 gold badges15 silver badges18 bronze badges

3

I’m not a JavaScript expert, but I wanted to see how these methods stack up, so this was good practice for me. I don’t know if this is technically the right way to performance test these, but I just ran them one right after another, as you can see in my code.

Sorting and getting the 0th value is by far the worst method (and it modifies the order of your array, which may not be desirable). For the others, the difference is negligible unless you’re talking millions of indices.

Average results of five runs with a 100,000-index array of random numbers:

  • reduce took 4.0392 ms to run
  • Math.max.apply took 3.3742 ms to run
  • sorting and getting the 0th value took 67.4724 ms to run
  • Math.max within reduce() took 6.5804 ms to run
  • custom findmax function took 1.6102 ms to run

var performance = window.performance

function findmax(array)
{
    var max = 0,
        a = array.length,
        counter

    for (counter=0; counter<a; counter++)
    {
        if (array[counter] > max)
        {
            max = array[counter]
        }
    }
    return max
}

function findBiggestNumber(num) {
  var counts = []
  var i
  for (i = 0; i < num; i++) {
      counts.push(Math.random())
  }

  var a, b

  a = performance.now()
  var biggest = counts.reduce(function(highest, count) {
        return highest > count ? highest : count
      }, 0)
  b = performance.now()
  console.log('reduce took ' + (b - a) + ' ms to run')

  a = performance.now()
  var biggest2 = Math.max.apply(Math, counts)
  b = performance.now()
  console.log('Math.max.apply took ' + (b - a) + ' ms to run')

  a = performance.now()
  var biggest3 = counts.sort(function(a,b) {return b-a;})[0]
  b = performance.now()
  console.log('sorting and getting the 0th value took ' + (b - a) + ' ms to run')

  a = performance.now()
  var biggest4 = counts.reduce(function(highest, count) {
        return Math.max(highest, count)
      }, 0)
  b = performance.now()
  console.log('Math.max within reduce() took ' + (b - a) + ' ms to run')

  a = performance.now()
  var biggest5 = findmax(counts)
  b = performance.now()
  console.log('custom findmax function took ' + (b - a) + ' ms to run')
  console.log(biggest + '-' + biggest2 + '-' + biggest3 + '-' + biggest4 + '-' + biggest5)

}

findBiggestNumber(1E5)

Peter Mortensen's user avatar

answered May 19, 2015 at 19:15

redOctober13's user avatar

redOctober13redOctober13

3,6436 gold badges34 silver badges61 bronze badges

2

I’ve found that for bigger arrays (~100k elements), it actually pays to simply iterate the array with a humble for loop, performing ~30% better than Math.max.apply():

function mymax(a)
{
    var m = -Infinity, i = 0, n = a.length;

    for (; i != n; ++i) {
        if (a[i] > m) {
            m = a[i];
        }
    }

    return m;
}

Benchmark results

answered Dec 10, 2012 at 2:57

Ja͢ck's user avatar

Ja͢ckJa͢ck

170k38 gold badges263 silver badges309 bronze badges

1

You could sort the array in descending order and get the first item:

[267, 306, 108].sort(function(a,b){return b-a;})[0]

answered Sep 4, 2009 at 14:20

Gumbo's user avatar

GumboGumbo

640k108 gold badges774 silver badges843 bronze badges

8

Use:

var arr = [1, 2, 3, 4];

var largest = arr.reduce(function(x,y) {
    return (x > y) ? x : y;
});

console.log(largest);

Peter Mortensen's user avatar

answered May 5, 2015 at 18:47

brroshan's user avatar

brroshanbrroshan

1,64017 silver badges19 bronze badges

2

Use Array.reduce:

[0,1,2,3,4].reduce(function(previousValue, currentValue){
  return Math.max(previousValue,currentValue);
});

Peter Mortensen's user avatar

answered Apr 10, 2014 at 15:11

CodeToad's user avatar

CodeToadCodeToad

4,6466 gold badges41 silver badges53 bronze badges

3

Finding max and min value the easy and manual way. This code is much faster than Math.max.apply; I have tried up to 1000k numbers in array.

function findmax(array)
{
    var max = 0;
    var a = array.length;
    for (counter=0;counter<a;counter++)
    {
        if (array[counter] > max)
        {
            max = array[counter];
        }
    }
    return max;
}

function findmin(array)
{
    var min = array[0];
    var a = array.length;
    for (counter=0;counter<a;counter++)
    {
        if (array[counter] < min)
        {
            min = array[counter];
        }
    }
    return min;
}

mgthomas99's user avatar

mgthomas99

5,1343 gold badges18 silver badges21 bronze badges

answered Nov 26, 2013 at 10:37

Yaser Ranjha's user avatar

1

Simple one liner

[].sort().pop()

answered Oct 7, 2019 at 9:45

YasirAzgar's user avatar

YasirAzgarYasirAzgar

1,35516 silver badges15 bronze badges

Almost all of the answers use Math.max.apply() which is nice and dandy, but it has limitations.

Function arguments are placed onto the stack which has a downside – a limit. So if your array is bigger than the limit it will fail with RangeError: Maximum call stack size exceeded.

To find a call stack size I used this code:

var ar = [];
for (var i = 1; i < 100*99999; i++) {
  ar.push(1);
  try {
    var max = Math.max.apply(Math, ar);
  } catch(e) {
    console.log('Limit reached: '+i+' error is: '+e);
    break;
  }
}

It proved to be biggest on Firefox on my machine – 591519. This means that if you array contains more than 591519 items, Math.max.apply() will result in RangeError.

The best solution for this problem is iterative way (credit: https://developer.mozilla.org/):

max = -Infinity, min = +Infinity;

for (var i = 0; i < numbers.length; i++) {
  if (numbers[i] > max)
    max = numbers[i];
  if (numbers[i] < min)
    min = numbers[i];
}

I have written about this question on my blog here.

Peter Mortensen's user avatar

answered Oct 3, 2013 at 19:22

lukas.pukenis's user avatar

lukas.pukenislukas.pukenis

13k12 gold badges47 silver badges81 bronze badges

2

Yes, of course there exists Math.max.apply(null,[23,45,67,-45]) and the result is to return 67.

Peter Mortensen's user avatar

answered Feb 1, 2015 at 15:34

user3702000's user avatar

Don’t forget that the wrap can be done with Function.prototype.bind, giving you an “all-native” function.

var aMax = Math.max.apply.bind(Math.max, Math);
aMax([1, 2, 3, 4, 5]); // 5

answered Oct 11, 2013 at 1:37

Paul S.'s user avatar

Paul S.Paul S.

64.4k9 gold badges120 silver badges138 bronze badges

You could also extend Array to have this function and make it part of every array.

Array.prototype.max = function(){return Math.max.apply( Math, this )};
myArray = [1,2,3];

console.log( myArray.max() );

dee-see's user avatar

dee-see

23.5k5 gold badges58 silver badges91 bronze badges

answered Jul 26, 2014 at 18:21

Izz's user avatar

3

You can also use forEach:

var maximum = Number.MIN_SAFE_INTEGER;

var array = [-3, -2, 217, 9, -8, 46];
array.forEach(function(value){
  if(value > maximum) {
    maximum = value;
  }
});

console.log(maximum); // 217

answered Sep 30, 2016 at 13:57

Benny Code's user avatar

Benny CodeBenny Code

50.7k28 gold badges229 silver badges198 bronze badges

Using – Array.prototype.reduce() is cool!

[267, 306, 108].reduce((acc,val)=> (acc>val)?acc:val)

where acc = accumulator and val = current value;

var a = [267, 306, 108].reduce((acc,val)=> (acc>val)?acc:val);

console.log(a);

answered Apr 12, 2017 at 6:02

arora's user avatar

aroraarora

8597 silver badges12 bronze badges

A recursive approach on how to do it using ternary operators

const findMax = (arr, max, i) => arr.length === i ? max :
  findMax(arr, arr[i] > max ? arr[i] : max, ++i)

const arr = [5, 34, 2, 1, 6, 7, 9, 3];
const max = findMax(arr, arr[0], 0)
console.log(max);

answered Oct 2, 2019 at 22:06

EugenSunic's user avatar

EugenSunicEugenSunic

13.1k11 gold badges64 silver badges82 bronze badges

You can try this,

var arr = [267, 306, 108];
var largestNum = 0;
for(i=0; i<arr.length; i++) {
   if(arr[i] > largest){
     var largest = arr[i];
   }
}
console.log(largest);

Peter Mortensen's user avatar

answered Jun 7, 2017 at 11:08

Aasha joney's user avatar

Aasha joneyAasha joney

4984 silver badges23 bronze badges

I just started with JavaScript, but I think this method would be good:

var array = [34, 23, 57, 983, 198];
var score = 0;

for(var i = 0; i = array.length; i++) {
  if(array[ i ] > score) {
    score = array[i];
  }
}

Peter Mortensen's user avatar

answered Jul 5, 2017 at 11:54

Jakub Karki's user avatar

Jakub KarkiJakub Karki

731 gold badge2 silver badges6 bronze badges

1

var nums = [1,4,5,3,1,4,7,8,6,2,1,4];
nums.sort();
nums.reverse();
alert(nums[0]);

Simplest Way:

var nums = [1,4,5,3,1,4,7,8,6,2,1,4]; nums.sort(); nums.reverse(); alert(nums[0]);

answered Nov 3, 2020 at 6:44

Siddhartha Mahato's user avatar

Run this:

Array.prototype.max = function(){
    return Math.max.apply( Math, this );
};

And now try [3,10,2].max() returns 10

answered May 17, 2017 at 12:32

RegarBoy's user avatar

RegarBoyRegarBoy

3,1681 gold badge21 silver badges44 bronze badges

Find Max and Min value using Bubble Sort

    var arr = [267, 306, 108];

    for(i=0, k=0; i<arr.length; i++) {
      for(j=0; j<i; j++) {
        if(arr[i]>arr[j]) {
          k = arr[i];
          arr[i] = arr[j];
          arr[j] = k;
        }
      }
    }
    console.log('largest Number: '+ arr[0]);
    console.log('Smallest Number: '+ arr[arr.length-1]);

answered Jul 11, 2018 at 6:23

Manoj's user avatar

ManojManoj

1,9282 gold badges15 silver badges20 bronze badges

1

Try this

function largestNum(arr) {
  var currentLongest = arr[0]

  for (var i=0; i< arr.length; i++){
    if (arr[i] > currentLongest){
      currentLongest = arr[i]
    }
  }

  return currentLongest
}

answered Dec 27, 2018 at 11:37

Toufiq's user avatar

ToufiqToufiq

1,07612 silver badges16 bronze badges

1

As per @Quasimondo’s comment, which seems to have been largely missed, the below seems to have the best performance as shown here: https://jsperf.com/finding-maximum-element-in-an-array. Note that while for the array in the question, performance may not have a significant effect, for large arrays performance becomes more important, and again as noted using Math.max() doesn’t even work if the array length is more than 65535. See also this answer.

function largestNum(arr) {
    var d = data;
    var m = d[d.length - 1];
    for (var i = d.length - 1; --i > -1;) {
      if (d[i] > m) m = d[i];
    }
    return m;
}

answered Mar 4, 2019 at 9:16

James Ray's user avatar

James RayJames Ray

4043 silver badges15 bronze badges

One for/of loop solution:

const numbers = [2, 4, 6, 8, 80, 56, 10];


const findMax = (...numbers) => {
  let currentMax = numbers[0]; // 2

  for (const number of numbers) {
    if (number > currentMax) {
      console.log(number, currentMax);
      currentMax = number;
    }
  }
  console.log('Largest ', currentMax);
  return currentMax;
};

findMax(...numbers);

answered Apr 15, 2020 at 20:44

Mile Mijatović's user avatar

Mile MijatovićMile Mijatović

2,8322 gold badges22 silver badges41 bronze badges

Find the largest number in a multidimensional array

var max = [];

for(var i=0; arr.length>i; i++ ) {

   var arra = arr[i];
   var largest = Math.max.apply(Math, arra);
   max.push(largest);
}
return max;

Peter Mortensen's user avatar

answered Aug 11, 2015 at 8:00

Liveindream's user avatar

4

My solution to return largest numbers in arrays.

const largestOfFour = arr => {
    let arr2 = [];
    arr.map(e => {
        let numStart = -Infinity;
        e.forEach(num => {
            if (num > numStart) {
                numStart = num;

            }
        })
        arr2.push(numStart);
    })
    return arr2;
}

answered Aug 2, 2020 at 11:14

Atudosiea Catalin's user avatar

Should be quite simple:

var countArray = [1,2,3,4,5,1,3,51,35,1,357,2,34,1,3,5,6];

var highestCount = 0;
for(var i=0; i<=countArray.length; i++){    
    if(countArray[i]>=highestCount){
    highestCount = countArray[i]
  }
}

console.log("Highest Count is " + highestCount);

answered Feb 17, 2021 at 17:21

Praveen Kumar's user avatar

Имеется такой динамический код html. data-id постоянно меняется. Нужно найти максимальное число data-id. В данном коде это 12.

<div class="mes-cont" id="chat-window">
  <div class="frt " id="pm1" data-id="1">
    <p>ytut</p>
  </div>
  <div class="frt " id="pm9" data-id="9">
    <p>ytut</p>
  </div>
  <div class="frt " id="pm10" data-id="10">
    <p>kjjj</p>
  </div>
  <div id="new_id"></div>
  <div class="frt " id="pm11" data-id="11">
    <p>kjjj</p>
  </div>
  <div class="frt " id="pm12" data-id="12">
    <p>kjjj</p>
  </div>
</div>

задан 10 дек 2016 в 15:17

skillful's user avatar

Вот так :

var max = 0 , id;
$(".frt").each(function(){
   id = $(this).data('id'); 
   max = (max < id) ?id:max; 
})

console.log(max)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="mes-cont" id="chat-window">
  <div class="frt " id="pm1" data-id="1">
    <p>ytut</p>
  </div>
  <div class="frt " id="pm9" data-id="9">
    <p>ytut</p>
  </div>
  <div class="frt " id="pm12" data-id="25">
    <p>kjjj</p>
  </div>
  <div class="frt " id="pm10" data-id="10">
    <p>kjjj</p>
  </div>
  <div id="new_id"></div>
  <div class="frt " id="pm11" data-id="11">
    <p>kjjj</p>
  </div>
  <div class="frt " id="pm12" data-id="12">
    <p>kjjj</p>
  </div>
</div>

ответ дан 10 дек 2016 в 15:35

Redr01d's user avatar

Redr01dRedr01d

1,63810 серебряных знаков15 бронзовых знаков

0

Есть еще вариант:

var id = Math.max.apply(null, $(".frt").map(function(i, el) {
  return parseInt($(el).attr("data-id"));
}).get())
var id = Math.max.apply(null, $(".frt").map(function(i, el) {
  return parseInt($(el).attr("data-id"));
}).get())

console.log(id)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="mes-cont" id="chat-window">
  <div class="frt " id="pm1" data-id="1">
    <p>ytut</p>
  </div>
  <div class="frt " id="pm9" data-id="9">
    <p>ytut</p>
  </div>
  <div class="frt " id="pm12" data-id="25">
    <p>kjjj</p>
  </div>
  <div class="frt " id="pm10" data-id="10">
    <p>kjjj</p>
  </div>
  <div id="new_id"></div>
  <div class="frt " id="pm11" data-id="11">
    <p>kjjj</p>
  </div>
  <div class="frt " id="pm12" data-id="12">
    <p>kjjj</p>
  </div>
</div>

ответ дан 10 дек 2016 в 16:01

C.Raf.T's user avatar

C.Raf.TC.Raf.T

4,0341 золотой знак10 серебряных знаков20 бронзовых знаков

1

В CSS есть математические функции, их всего четыре: min(), max(), clamp() и calc().

Математика в каком-то виде есть и в препроцессорах, например, в SCSS есть min(), max() и clamp(), в Less — min() и max(), но есть пара существенных отличий. Во-первых, в препроцессорах всё рассчитывается один раз при компиляции, и в итоговом файле всегда будут фиксированные значения. А во-вторых, в препроцессорных функциях нельзя сочетать разные единицы измерения.

Например, не получится сделать значение на основе пикселей, зависящее от единиц вьюпорта. Вот этот пример работать не будет (это SCSS):

$base-font-size: 16px;

BODY {
  font-size: $base-font-size + 2vw;
}

Компилятор будет ругаться на несочетаемые единицы.

Но если перенести рассчеты в calc(), всё будет работать:

$base-font-size: 16px;

BODY {
  font-size: calc(#{$base-font-size} + 2vw);
}

calc() сможет сложить пиксели и единицы вьюпорта. Это позволит сделать плавающий размер шрифта, который будет зависеть от ширины окна браузера. Препроцессоры так не умеют.

Таким образом, главное преимущество математических функций в CSS — динамичность значений благодаря способности сочетать абсолютные и относительные единицы измерения.

Ещё важно помнить, что при использовании препроцессоров CSS-функции могут конфликтовать с препроцессорными. Например, если в SCSS или в Less написать такое:

компилятор будет ругаться на несовместимые единицы, потому что сработает функция из препроцессора.

Чтобы препроцессоры при компиляции не пытались выполнить CSS-функции, в SCSS предлагается писать имя функции с заглавной буквы:

Подробнее об этом можно почитать в статье Аны Тюдор When Sass and New CSS Features Collide.

В LESS поможет оборачивание кавычками:

width: ~"min(350px, 50%)";

или компиляция с использованием параметра math со значением strict.

min(), max()

Функция min() возвращает минимальное из переданных значений, max() — максимальное. При использовании процентов или относительных единиц выбираемое значение будет динамическим и будет зависеть от внешних условий. Например:

Если поресайзить окно с примером, можно увидеть как это работает.

Если вычисленное значение для 50% меньше 350px, ширина блока будет равна 50%. Как только 50% станет больше 350px, функция min() выберет меньшее значение, то есть 350px, и дальше блок тянуться не будет.

Функция min() будет выбирать подходящее значение учитывая ширину окна в данный момент.

Этот код не делает ничего особенного, что не умел бы CSS без математических функций: точно такое же поведение можно получить задав width и max-width:

width: 50%;
max-width: 350px;

Оба варианта для сравнения:

Порастягивайте демо, чтобы увидеть, что разницы в поведении блоков нет.

На первый взгляд, min() и max() не делают ничего интересного, но если подумать, как много мест в CSS, где можно управлять диапазоном значений?

Это доступно только для ширины и высоты:

  • width, min-width, max-width
  • height, min-height, max-height

и их аналогов, привязанных к направлению письма:

  • inline-size, min-inline-size, max-inline-size
  • block-size, min-block-size, max-block-size

У нас нет, например, max-font-size или min-border-width, но благодаря математическим функциям, можно добавить умную динамику практически в любое значение. Например, в размер шрифта:

font-size: max(5vw, 20px);

Поресайзите демо, и вы увидите, что при растягивании шрифт будет бесконечно расти, но при сужении, как только 5vw станет меньше или равным 20px, размер шрифта застынет на этом значении.

Если попытаться реализовать подобное на JS, пришлось бы отслеживать ресайз окна, получать вычисленное значение, и при превышении заданного порога, фиксировать значение на определённом уровне. При использовании min() и max() браузер всё делает сам: отслеживает изменение значений и, при необходимости, фиксирует их — нам нужно просто выбрать функцию и задать диапазон значений.

Это будет работать во всех свойствах, которые используют размеры, в том числе в тенях, градиентах и прочем. Пример с box-shadow:

box-shadow: 0 0 max(45px, 15vw) rgba(0, 0, 0, .5);

Размер тени будет зависеть от размера окна браузера, минимальное значение — 45px.

Экспериментируя с функциями min() и max() можно заметить, что они дают возможность управлять или минимальным значением, или максимальным, но нельзя контролировать и то, и другое одновременно. Хотя совершенно логичным выглядит желание, например, задать размер шрифта, который будет расти в диапазоне от минимального значения до максимального и не выходить за эти значения.

Для такого есть ещё одна функция:

clamp()

Она сочетает в себе min() и max(). Функция получает на вход параметры:

И вычисляет значение вот таким образом:

Проще всего её понимать представляя среднее значение (VAL) как желаемое, которое ограничено минимальным и максимальным значениями. Например, этот код

font-size: clamp(20px, 5vw, 40px);

описывает следующее поведение: размер шрифта равен 5vw, но не меньше 20px и не больше 40px. Порастягивайте демо, чтобы увидеть как это работает:

Аналогичный подход часто используется при разработке адаптивных сайтов: мы задаём минимальное и максимальное значения ширины страницы, позволяя ей тянуться и сжиматься в заданных пределах, например, в диапазоне от 320px до 1200px:

min-width: 320px;
max-width: 1200px;

Используя clamp() это можно записать в одну строчку:

width: clamp(320px, 100%, 1200px);

Для следующего демо я взяла значения поменьше, но смысл тот же: блоки тянутся в пределах от 200px до 400px. Ширина верхнего блока управляется с помощью min-width и max-width, нижнего — с помощью clamp():

Оба блока ведут себя совершенно одинаково, разница только в возможностях этих подходов: clamp() позволяет добавить умную динамику значений в любое свойство, не только в размеры блоков. Мне нравится идея использовать clamp() для адаптивных шрифтов, почитать про это можно в статье Linearly Scale font-size with CSS clamp() Based on the Viewport.

Предыдущие функции достаточно просты по сравнению с calc(), самой мощной и интересной.

calc()

Функция позволяет производить математические операции:

  • сложение
  • вычитание
  • умножение
  • деление

В отличие от препроцессорных вычислений, calc() позволяет сочетать любые совместимые значения. Например, можно вычесть пиксели из процентов:

width: calc(100% - 20px);

Препроцессоры так не умеют, потому что на момент компиляции CSS неизвестно чему равны 100%, но у браузера эти данные есть, следовательно, он может взять 100%, перевести их в пиксели и вычесть из них 20px. Кроме того, браузер пересчитает значение, если ширина элемента изменится.

Внутри calc() можно использовать кастомные свойства, и это делает вычисления в CSS гораздо мощнее препроцессорных. Например, на calc() и кастомных свойствах можно без каких-либо скриптов сделать простой генератор цветовых схем:

По клику на цвет в CSS меняется значение CSS-переменной, и вся палитра пересчитывается.

Рассчёт оттенка (hue из HSL) делается таким образом:

--palette-hue-2: calc(var(--palette-hue) + var(--palette-step-1));

И затем оттенок используется для формирования цвета в формате HSL:

background: hsl(var(--palette-hue), var(--base-sat), var(--base-lght));

Конечно, подобные вещи удобнее и логичнее делать на JS, демо просто показывает, что CSS достаточно мощный, чтобы уметь такое.

Примеры использования

Cохранение логики рассчётов

Например, если нужна ширина в 1/12 от общей, можно высчитать значение и записать его в стили:

Но так непонятно что это за число. Станет немного понятнее, если логику рассчёта положить рядом в комментарии:

.block {
  width: 8.33%; /* 100% / 12 */
}

А если использовать calc(), можно само математическое выражение сделать значением:

.block {
  width: calc(100% / 12);
}

Так сразу понятно, что элемент занимает 1/12 ширины родителя, и не нужно поддерживать комментарии в актуальном состоянии.

Управление размерами элементов

Например, есть карточка с картинкой, картинка ужимается под ширину колонки с текстом:

.card {
  min-width: 300px;
  max-width: 400px;
  padding: 1rem;
}

.card__img {
  width: 100%;
}

Как растянуть картинку на ширину карточки не привязываясь к размерам карточки? С помощью calc():

.card {
  min-width: 300px;
  max-width: 400px;
  padding: 1rem;
}

.card__img {
  /* Ширина картинки */
  width: calc(100% + 2rem);
  /* Сдвиг влево на размер паддинга */
  margin-left: -1rem;
}

Можно ещё немного улучшить код, используя кастомные свойства:

.card {
  --padding: 1rem;

  min-width: 300px;
  max-width: 400px;
  padding: var(--padding);
}

.card__img {
  width: calc(100% + var(--padding) * 2);
  margin-left: calc(var(--padding) * -1);
}

Так сохранится логика рассчётов, 1rem становится не магическим числом, а именованной переменной, и по самому коду будет понятно, что ширина картинки увеличивается на два паддинга, а потом сдвигается влево на один. Можно примерно понять что делает этот код даже не глядя на страницу в браузере.

Ещё это будет полезно для инпутов. Допустим, нужно, чтобы инпут тянулся на всю ширину родителя, оставляя 40 пикселей справа. Вариант с фиксированной шириной не подходит для адаптивного дизайна. Если просто задать ширину 100% и марджин, блок растянется на всю ширину и сжиматься не будет. С этой задачей прекрасно справится calc():

.input {
  width: calc(100% - 40px);
}

Текстовое поле всегда будет отодвинуто от края на 40px (+ паддинг) независимо от размера родительского элемента.

Адаптивная типографика

С помощью calc() можно примешивать единицы вьюпорта к обычному размеру шрифта, тогда при увеличении размеров экрана будет расти и шрифт:

BODY {
  font-size: calc(1rem + .5vw);
  line-height: calc(1.2rem + .5vw);
}

Порастягивайте демо, чтобы увидеть как размер окна влияет на размер шрифта:

Почитать об этом больше можно тут или тут.

Мне больше нравится вариант с clamp(), который не только позволяет шрифту расти, но и задаёт ему верхнюю и нижнюю границы.

Управление размерами фона

С помощью calc() можно задать размер фоновому изображению, комбинируя фиксированные единицы и проценты. Например, чтобы фон всегда отступал от краёв на определённое значение:

DIV {
  --offset: 1rem;
  --bg-size: calc(100% - var(--offset) * 2);

  /* Цветной градиент */
  background: linear-gradient(45deg, crimson, 20%, gold, 80%, turquoise) no-repeat;
  /* Размер фона */
  background-size: var(--bg-size) var(--bg-size);
  /* Отступ от края элемента */
  background-position: var(--offset) var(--offset);
}

Полосатые поля показывают прозрачные области вокруг разноцветного градиента.

Используя этот же подход можно делать вырезанные углы квадратной формы:

DIV {
  --offset: 1rem;
  --bg-size: calc(100% - var(--offset) * 2);

  /* Однотонный фон из двух градиентов */
  background:
    linear-gradient(turquoise, turquoise),
    linear-gradient(turquoise, turquoise);
  background-repeat: no-repeat;
  /* Один фон сжат по вертикали, другой по горизонтали */
  background-size:
    100% var(--bg-size),
    var(--bg-size) 100%;
  /* Один фон сдвинут по вертикали, другой по горизонтали */
  background-position:
    0 var(--offset),
    var(--offset) 0;
}

В отличие от версии с коническими градиентами, этот вариант будет работать во всех браузерах. Способ был предложен Ильёй Стрельциным.

Также можно рисовать линейными градиентами полосы заданной ширины:

DIV {
  --line-witdh: 3rem;

  background:
    linear-gradient(
      to left top,
      transparent calc(50% - var(--line-witdh) / 2),
      turquoise 0, turquoise calc(50% + var(--line-witdh) / 2),
      transparent 0
    );
}

Вычисление цветов и шагов градиента

Иногда для экспериментов нужен полосатый градиент. Чтобы не считать параметры руками, их можно вычислять с помощью calc() на основе кастомных свойств. Вот так задаются параметры:

--steps: 9;
--hue-step: calc(360deg / var(--steps));
--line-width: 1rem;

А вот так потом высчитывается оттенок:

hsl(calc(var(--hue-step) * 2), 100%, 63%)

И точка остановки:

calc(var(--line-width) * 7)

Редактируя переменные можно менять параметры градиента без необходимости пересчитывать вручную шаги или переписывать весь градиент.

Правда, при таком автоматическим вычислении оттенков могут получаться не очень красивые цвета, но для экспериментов вполне пойдёт.

Область применения cacl() гораздо шире перечисленных примеров. В некоторых случаях, если значения не должны меняться динамически, с подобными задачами справятся и препроцессоры, но если должны, например, при изменении кастомных свойств или размера окна, — без calc() не обойтись.

Нюансы

При использовании calc() нужно помнить о некоторых тонкостях:

  • арифметический знак всегда нужно окружать пробелами, чтобы браузер мог правильно разобрать выражение. Например, width: calc(100% -50px) не будет работать, правильно так: width: calc(100% - 50px). Для умножения и деления это требование не является обязательным, но лучше всегда добавлять пробелы, чтобы сохранить код единообразным и легко читаемым;
  • делить на ноль нельзя, в результате будет ошибка;
  • calc() умеет производить рассчёты только для совместимых единиц. Например, можно сложить пиксели с процентами или единицами вьюпорта, градусы с радианами и вычесть миллисекунды из секунд, но вот сложить секунды с пикселями или градусы с единицами вьюпорта ожидаемо не получится, потому что непонятно в каких единицах ожидается результат.

    У меня была слегка безумная идея привязать градусы градиента к ширине вьюпорта (и вращать градиент растягивая окно браузера), но я не придумала как это можно реализовать, и не уверена, что это в принципе возможно без JS;

  • рассчёты в медиавыражениях поддерживаются только для одинаковых единиц: пиксели можно складывать с пикселями, ремы с ремами. Складывать пиксели с ремами внутри медиавыражения нельзя. Кастомные свойства внутри медиавыражений не работают вообще, и никакие вычисления с ними, соответственно, невозможны (демо с calc() в @media). Для каких-то сложных конструкций можно попробовать воспользоваться логическими операциями.

Светлое будущее?

В спецификации есть интересный момент: утверждается, что внутри calc() в качестве значений можно использовать содержимое атрибутов, да и в принципе можно использовать атрибуты как значения CSS-свойств.

От обычного использования attr() в качестве содержимого псевдоэелемента это отличается указанием типа содержимого:

/* HTML
<div data-color="#FC9">...</div>
*/

background: attr(data-color color, orange);

Или единиц измерения:

/* HTML
<div data-length="300">...</div>
*/

width: attr(data-length px, 200px);

Значение после запятой — запасное, на случай, если не удастся получить или распарсить значение атрибута.

Поначалу эта конструкция показалась мне странной: зачем класть данные в атрибуты, а потом ещё и типизировать их в CSS, если можно сразу положить нужное в кастомные свойства?

Например:

/* HTML
<div style="--width: 100px"></div>
*/

DIV {
  width: var(--width);
  height: 50px;
  background: gold;
}

Но в твиттере мне быстро накидали примеров, когда это было бы действительно удобно. Например, получая размеры из атрибутов было бы удобно делать адаптивные картинки и фреймы. У меня получилось сделать адаптивное видео, используя кастомные свойства, но для этого приходится дублировать размеры в инлайновых стилях. Если бы можно было считывать значения из атрибутов, без дублирования можно было бы обойтись.

На момент написания статьи это не поддерживается ни одним браузером, но есть некоторая надежда, что однажды это заработает, потому что подобное использование атрибутов описано в спецификации свойства, которое помогло бы управлять соотношением сторон: aspect-ratio. Там есть такой пример:

/* HTML
<iframe
  src="https://www.youtube.com/embed/0Gr1XSyxZy0"
  width=560
  height=315>
*/

@supports (aspect-ratio: attr(width number) / 1) {
  iframe {
    aspect-ratio: attr(width number) / attr(height number);
    width: 100%;
    height: auto;
  }
}

И вот это было бы очень круто, потому что избавило бы разработчиков от необходимости городить странные конструкции для решения той же задачи. Почитать про aspect-ratio можно в статье Рэйчел Эндрю Designing An Aspect Ratio Unit For CSS.

Также не так давно в черновики были добавлены другие математические функции, например, mod(), round(), sin() и многие другие. Это совсем свежее добавление, новые функции ещё нигде не поддерживаются.

Поддержка браузерами

min(), max(), clamp() и calc() поддерживаются в большинстве современных браузеров, их поддержка примерно совпадает с кастомными свойствами. И то, и другое не работает в IE11.

При необходимости проверить поддержку функций и кастомных свойств можно воспользоваться @supports (также учитывая поддержку браузерами для него):

@supports (--color: pink) {
  ...
}

@supports (width: min(10px, 20px)) {
  ...
}

Поиск максимального числа

Чтобы найти максимальное число в JavaScript
используйте Math.max.
Смотрите пример:

console.log(Math.max(10, 45, 50, 344, 30));

Результат выполнения кода:

344

Максимальный элемент в массиве через apply

Хитрым путем можно найти максимальное значение
массива, используя Math.max
в связке с методом apply:

let arr = [10, 45, 50, 344, 30];
console.log(Math.max.apply(null, arr));

Результат выполнения кода:

344

Максимальный элемент в массиве через spread

Использование метода apply
для нахождения минимального элемента в массиве
считается устаревшим с появлением оператора

spread. Давайте посмотрим пример:

let arr = [10, 45, 50, 344, 30];
console.log(Math.max(...arr));

Результат выполнения кода:

344

Смотрите также

  • урок
    нахождение минимального числа

Добавить комментарий