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
156k34 gold badges229 silver badges234 bronze badges
asked Sep 4, 2009 at 14:17
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.
answered Sep 4, 2009 at 14:19
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.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)
answered May 19, 2015 at 19:15
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͢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
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);
answered May 5, 2015 at 18:47
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);
});
answered Apr 10, 2014 at 15:11
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
5,1343 gold badges18 silver badges21 bronze badges
answered Nov 26, 2013 at 10:37
1
Simple one liner
[].sort().pop()
answered Oct 7, 2019 at 9:45
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.
answered Oct 3, 2013 at 19:22
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
.
answered Feb 1, 2015 at 15:34
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.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
23.5k5 gold badges58 silver badges91 bronze badges
answered Jul 26, 2014 at 18:21
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 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
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
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);
answered Jun 7, 2017 at 11:08
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];
}
}
answered Jul 5, 2017 at 11:54
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
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
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
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
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 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ć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;
answered Aug 11, 2015 at 8:00
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
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
Имеется такой динамический код 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
Вот так :
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
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.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
Смотрите также
-
урок
нахождение минимального числа