I need to set for a div a margin-left in JavaScript in such a way that its value if the minimum between two lengths in different units, e.g. min(10pt,15vw).
There is Math.min() that we can use to compare numbers. Is there a similar function for lengths?
Alternatively, is there a way to convert length from one unit to another?
The minimum of two lengths with separate units can be calculated by creating a temporary hidden div and using the offsetWidth property to calculate the layout width of the element in pixels (followed by immediate removal of the div):
const minimum = (a, b) => {
const temp = document.createElement('div');
const size = [];
temp.style.padding = 0;
temp.style.border = 0;
temp.style.overflow = 'hidden';
temp.style.visibility = 'hidden';
document.body.appendChild(temp);
temp.style.width = a;
size[0] = temp.offsetWidth;
temp.style.width = b;
size[1] = temp.offsetWidth;
temp.parentNode.removeChild(temp);
return Math.min(size[0], size[1]) - size[0] ? b : a;
};
console.log('The minimum width is:', minimum('100px', '100%'));
As shown above, you can use minimum('100px', '100%') to return the minimum value.
Since 1vw reflects 1% of the viewport's width, you can convert it to pixels using window.innerWidth:
var 1vw = window.innerWidth * 0.01;
and comparing that with 15 pixels:
Math.min(1vw * 15, 10);
You can use simple comparison operators for length comparison like =, <=, >= etc. But only when you have same units. The other part of your questions, you can use a small js library for that, called math.js.
The math.js library comes with support for units. The library does not yet support derived units but that is on the planning.
http://mathjs.org/
Related
I am using react-masonry-css to layout some images in a masonry-like layout, but it just is putting the same number of images in each column basically, and it ends up having large discrepancies in the amount of space left at the bottom of each column, as seen here (this is the bottom of all the columns):
(Note, a couple images in this demo are missing, but even if they are all present there are large differences in the bottom space remaining).
It looks like all that library does is put equal number of items in each column, irrespective of the image heights.
I would like for it to distribute the images more optimally, given that I also have included the width and height for each image in the JSON data. How can I use the height to figure out how to optimally place the images in a known number of columns? It seems extremely complicated at first thought, like it would require a sophisticated research-paper-like algorithm. Is that true? If so, what are the general algorithms used to solve this, so I may go about writing a quick implementation in JavaScript? If it's instead straightforward, how is it done?
In my head so far, I would maybe divide the number of images by number of columns to give the first rough estimate of how many per column. Then I would put one image in each column, so there are 1 image in each of the 7 columns. Then I would lay like bricks across the columns next images. I would do the best to keep the column heights the same high, searching through and selecting images that fit an appropriate height.
That is just brainstorming, but already I can see several holes and pitfalls in that non-workable approach. It seems quite hard, wondering if it's a solved problem though, given the images can be widely varied in height.
The end goal is to have all of the columns be roughly the same height, that's it. As close as possible.
If we operationalize “most compact” to mean the shortest bounding box,
then this is an identical-machines
scheduling
problem. Each image corresponds to a job, and each column corresponds to
a machine. The processing time of a job is the height/width ratio of the
corresponding image (plus padding).
Although scheduling is NP-hard, there’s a simple and provably effective
approximation known as Longest Processing-Time
First.
In terms of your problem, for each image in descending order of
height/width, you assign it to the column that is currently the
shortest. (You can shuffle the order of images in each column at the end
to avoid bias towards taller images.) The bounding box will never be
more than 34% longer than it needs to be (well, maybe a little more
because of the interstitials).
// Set up some test data.
const imageCount = 50;
let images = [];
for (let i = 0; i < imageCount; ++i) {
// Allow the images to vary in aspect ratio between 5:1 and 1:5.
images.push({
id: i,
width: Math.random() + 0.25,
height: Math.random() + 0.25,
});
}
// Parameters.
const columnCount = 10;
const thumbnailWidth = 100;
const interstitialHeight = 10;
// Algorithm begins. Initialize empty columns.
let columns = [];
let columnHeights = [];
for (let j = 0; j < columnCount; ++j) {
// This height will be correct once the column is nonempty.
columnHeights.push(-interstitialHeight);
columns.push([]);
}
// Sort the images by aspect ratio descending.
function aspectRatioDescending(a, b) {
return b.height / b.width - a.height / a.width;
}
images.sort(aspectRatioDescending);
// Assign each image to a column.
for (const image of images) {
// Find the shortest column.
let shortest = 0;
for (let j = 1; j < columnCount; ++j) {
if (columnHeights[j] < columnHeights[shortest]) {
shortest = j;
}
}
// Put the image there.
columnHeights[shortest] +=
interstitialHeight + thumbnailWidth * (image.height / image.width);
columns[shortest].push(image);
}
// Shuffle the columns for aesthetic reasons.
for (const column of columns) {
for (let k = 1; k < column.length; ++k) {
const i = Math.floor((k + 1) * Math.random());
let temp = column[i];
column[i] = column[k];
column[k] = temp;
}
}
const maxHeight = Math.max.apply(null, columnHeights);
const minHeight = Math.min.apply(null, columnHeights);
// Analyze the layout.
console.log(
"// The tallest column is %f%% taller than the shortest.",
(100 * (maxHeight - minHeight)) / minHeight
);
// The tallest column is 3.030982959129835% taller than the shortest.
I want to get user device display size with screen.width and screen.height on page load (for example: iPhone 7, 375x667px), then I need to compare these two sizes and use the smallest size (375px) to apply it to an element with CSS function.
function() {
var ww = screen.width;
var wh = screen.height;
}
I'm new to JavaScript so don't know how to do the second part, comparison and further manipulation.
How can it be done?
As said in comments by many people, we can't understant why not to use if.
You could have used conditional operators: ? and :, but it is in essentially an if.
You can also make use of the Math library with min() or max() functions to get the smallest or highest value of some values (can be used for arrays also, but it is not the case here). The usage: Math.min(value1, value2)
Example:
var ww = 100;
var wv = 120;
var smallest = Math.min(ww,wv);
console.log(smallest)
Further reading: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/min
To finish, Math.min() or max() also uses if in the internal code...
You can do it like this using jquery,
$(document).ready(function(){
var smallest;
var winwid = $(window).width();
var winheight = $(window).height();
if(winwid < winheight ){
smallest = winwid;
alert('Width is smaller than height: '+winwid);
}
else {
smallest = winheight;
alert('Height is smaller than width: '+winheight);
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
I'm implementing a system in react-native where I use the onScroll handler to detect the Y offset from the top of the viewport. When the current scroll position equals a number stored in a separate object, it has to be detected.
This is my code now:
onActivitiesScroll = (event) => {
let positionYTrack = _.find(
this.state.trackHeaderCardPositionsY,
{ positionY: Math.floor(event.nativeEvent.contentOffset.y) }
);
console.log(positionYTrack);
}
The problem is this part: { positionY: Math.floor(event.nativeEvent.contentOffset.y) }
The event.nativeEvent.contentOffset.y is an exact number (floored) and positionY is a property in a state object. The problem is that when I scroll, it skips many scroll positions because of the limited number of frames the onScroll handler handles.
What this code is doing right now is:
if Math.floor(event.nativeEvent.contentOffset.y) exactly equals one of the positionY property values in the state, it should log it. I want to do it like this:
if Math.floor(event.nativeEvent.contentOffset.y) is within the range of current position - 10 and current position + 10, it should detect it.
Any idea how this can be achieved? I just can't seem to find a solution for this, even after sleeping over it.
_.find also accepts a function to compare values. Here's a simple example you can use to find if y is in range:
onActivitiesScroll = (event) => {
let positionYTrack = _.find(
this.state.trackHeaderCardPositionsY,
position => {
const y = Math.floor(event.nativeEvent.contentOffset.y);
const min = y - 10;
const max = y + 10;
return min >= position <= max;
}
);
}
I have two divs side by side set to height auto. I want them to have equal height, so i combined them as members of an array.
I recurse through the array and set the not-tallest ones to the height of the tallest. Problem is everything i have tried to get the COMPUTED height has resulted in the incorrect value.
I have tried the following:
(els[x].currentStyle) ? h=els[x].currentStyle.height : h=window.getComputedStyle(els[x],null).height;
h = el.clientHeight || el.offsetHeight || el.scrollHeight;
Both of these are yielding 640 px'ish while the computed is 751.8 in my particular showing.
Is there possbily a constant I can use to get the correct height. Like maybe the number im getting would be on a standard size screen (like 960 pixels high or such) then multiple that by the window size?
I have had a lot of good use of this little function I came up with
function getH(id)
{
return document.getElementById(id).offsetHeight;
}
// I have a styles.js file where this function feels right at home. Access this function from anywhere.
This will return the height of any given elements given to the function (by it's ID). So now we'r 1/3 of the way.
Then use the Math.max() function to get the height of the largest element.
var maxH = Math.max(getH("ID1"),getH("ID2"));
This is 2/3 of the way, YAY - Now set the elements height to be the same.
var x = document.getElementById("ID1");
var y = document.getElementById("ID2");
x.style.height = maxH +"px";
y.style.height = maxH +"px"; //Remember the +"px" has to be added as a string, thats the reason for the "".
DONE!! - Now put it all together
I would imagine something like this
function setElementHeight()
{
var maxH = Math.max(getH("ID1"),getH("ID2"));
var x = document.getElementById("ID1");
var y = doc...("ID2");
x.style.height = maxH +"px";
y.style.height = maxH +"px";
}
// Don't forget to include the first function in you'r .js file
This WILL set both ID's to the same height and the height WILL be equal to the highest. That is what you want, isn't it?
--EDIT--
I would throw away the array if I were you. Just have the 2 DIV's each with a unique ID and make them equally tall based upon that.
Molle
If you have the DOM document, you can try the below code:
let divElement = document.getElementById('divId');
let height = document.defaultView.getComputedStyle(divElement).height;
'height' will have the exact height of the element with 'divId' once it is computed.
I have an array of SVG lines that represent the sides of a triangle, I want to color the lengthiest one (the hypotenuse). Assuming ab, bc and ca are the the SVG line elements...
var hypotenuse = [ab, bc, ca].sort(getLineLength)[0];
hypotenuse.setAttribute("stroke-width", 5);
function getLineLength(el) {
var x1 = el.getAttribute("x1");
var x2 = el.getAttribute("x2");
var y1 = el.getAttribute("y1");
var y2 = el.getAttribute("y2");
var xs = x2 - x1;
var ys = y2 - y1;
xs = xs * xs;
ys = ys * ys;
return Math.sqrt(xs + ys);
}
This doesn't work, it seems it grabs the first element always. It also doesn't look very intuitive. What is the correct way to achieve this in Javascript? I know how to do it in loops but I'd like to know if there's some functional idiom that allows me to achieve this.
JSFiddle is here: http://jsfiddle.net/btbkd/
Because function in array.sort(function) expects two arguments, and you should return an Integer which is
less than zero (indicates that the first one should be prior than the second one);
zero (indicates that the two is equal);
larger than zero (indicates that the second one is prior than the first one).
MDN Document
Also you forgot to reset the line width.
JSFiddle
Edit:
Noted that I get rid of the Math.sqrt part from your getLineLength function, because if comparing is all that you expect from this function, calculating square root is not necessary, and removing it may make it a little bit faster.
When you pass a function to sort(), that function has to take two arguments and return the result of comparing them (-1, 0, or 1), or if comparing numbers then you can just subtract the two arguments: Mozilla Array.sort Reference
A simple way to make your example work should be something like this:
var hypotenuse = [ab, bc, ca].sort(function(lineOne, lineTwo) {
return getLineLength(lineTwo) - getLineLength(lineOne);
})[0];
And then the rest like you already have. Note that the order of the subtraction is important, as it will be the difference between sorting from lowest to highest or from highest to lowest.
I don't remember JavaScript syntax, so the syntax may be wrong, but if I understand your question correctly no sort() should be necessary and you should be able to do:
var maxLength = 0;
var longestElement = -1;
for (var i = 0; i < array.length; i++) {
if( getLineLength(array[i]) > maxLength )
longestElement = i;
}
If longestElement is -1, there is no line with a length greater than 0. Otherwise, hypotenuse is array[longestElement].
Note that neither this nor the sort() example does any special handling for lines of equal length (e.g. an equilateral triangle).