How to Sort Images by Dimension - javascript

I try to find all images in a web page, add them in an array, check their dimensions and sort the array accordingly.
var images = document.getElementsByTagName("img");
var image_dimensions=[];
for(var i=0;i<images.length;i++){
image_dimensions.push(images[i].width+"x"+images[i].height);
}
//var image_dimensions = ["45x67", "68x45", "12x23", "124x90"];
I want to sort the images according to their dimensions and update the images array. I tried to push dimensions into image_dimensions and sort it but later I could not figure out how can I relate the dimensions to the actual img nodes.
How can I sort the images in a web page by dimension?

From the comments you've made I think what you actually want to sort by is the total area of each image (height * width). You can do this by using the sort method with a custom compare function.
var sortedImgs,
imgs = document.getElementsByTagName('img'); // returns a NodeList,
// an array-like object
imgs = [].slice.call(imgs); // convert into a real array
sortedImgs = imgs.sort(function (a, b) {
// Compute the area of each image
var aArea = a.width * a.height,
bArea = b.width * b.height;
// compare the area of each
return aArea - bArea;
});
The compare function subtracts the area of b from a and returns that value to the sort function:
If they have the same area this will return 0, telling sort they are equal
I If b is bigger than a, a negative number will be returned telling sort to sort a lower.
If a is bigger than b, a positive number will be returned telling sort to sort b lower.
Swaping aArea and bArea would flip those results, reversing the sort order.
The other answers have used clientWidth and clientHeight for some reason; clientWidth and clientHeight include any padding that might be on the element. (And as crazy as it is, images can have padding.) I used .width and .height, assuming you are interested in the actual dimensions of the image itself, not any styling that has been added to it.

Use a 2D array and include the image element too, something like this:
var images = document.getElementsByTagName('img');
var imageDimensions = [];
for(var i = 0; i < images.length; i++) {
imageDimensions.push([images[i].clientWidth, images[i].clientHeight, images[i]]);
}
imageDimensions.sort(function (a, b) {
if (a[0] - b[0] === 0) {
return a[1] - b[1];
}
return a[0] - b[0];
});
clientWidth/Height returns the size of the image as you can see on the page.
Result:
imageDimensions = [
[width, height, HTMLImageElement],
[width, height, HTMLImageElement],
:
]
And as adeneo has mentioned in a comment, you've to do this after the images have been fully loaded, put the code for example into window.onload event handler.

You can use clientWidth and clientHeight property to find the dimension as described here ..After finding the dimensions you can use the function .sort() for the sorting stuff.
Please have a look at this link for the reference.

Related

Grid of squares that fits within any width/height container

I am trying to create a grid of squares that could fit in any dimensions container (ie. the squares resize themselves or add/delete new ones if the container width/height are changed).
I am going the Javascript route for now (and Jquery, for now) - there might be a flexgrid solution but since I plan to populate my squares with some kind of cellular automata type thingie, I figured that it wouldn't hurt. This doesn't solve my problem, since the number of lines of squares seems to be fixed.
Here is what I have so far:
var screen = {
width: 0, // these I get with jquery, on load and on resize events
height: 0
}
var values = {
min: 100, // minimum square size
max: 500 // maximum square size
}
var findSize = function() {
var r = 1;
var currVal = values.min;
while (r > 0) {
if((screen.width % currVal) === (screen.height % currVal)) {
// this should mean that they are both divisible by this value, right?
// get out of the loop and return value
r = 0;
return currVal;
} else if (currVal > values.max ) {
// if we exceed the maximum size, get out of the loop and return 0
r = 0;
return 0;
} else {
// if not, increment a bit
currVal += 0.25; // increment the value to check the modulo against
}
}
}
Calling the findSize() function should return either the dimensions of the square (from which I can then build my grid easily, with either floated squares or absolutely positioned ones.
The problem is that it doesn't. Well it sometimes does. And it also pretty often gives me nothing...
The border are done with box-shadow so it shouldn't affect the dimensions.
So I am wondering...
Where is my code faulty?
Can I change my function so it return always something (maybe with smaller incrementations?). I can work with rounded values for display purpose.
The brute force aspect doesn't seem too efficient. Is there a way to refactor this so the loop is shorter?
Thanks a lot!
The problem with this code is it tries to find the solution but fails to account for answers between each deltas (0.25).
Also, if there can be any numbers of cells (added removed automatically) then the answer can also be "always 100".
I guess what you're trying to achieve here is a "best fit" that could leave no borders horizontally and vertically at the same time. I'm not sure if there is a proper answer to that question (you probably need to search for Greatest common divisor, close to what you are doing), and I wonder if something like the code below wouldn't work in your case:
var findSize = function() {
var ratio = screen.width / screen.height; // get the ratio between width and height
if (ratio > 1) {
ratio = 1 / ratio; // always have it always <= 1
}
var size = values.max * ratio; // size between 0 and max
if (size < values.min) {
return values.min; // failed, could try other things here
}
return size; // size between min and max
}

calling object.width is returning NaN in JavaScript

I am currently working on a game that has to do with a rocket ship moving around and objects(circles) are falling from the top. The goal of this game is to not hit the objects as they are falling down the screen. I am running into problems when writing my collision algorithm.
I have declared var hit = false; at the top of my code
I have also put all of the circles into an array called projectiles.
I believe that I have the logic correct but I discovered that when calling either p.width or ship.width it returns NaN. I have tried using offsetWidth and that didn't work either. I am wondering how else to go about getting the width of my objects
The else statement at the bottom is just to check if .width is returning the correct number. Once I get it to work it will be removed and replaced with the final parts of the collision algorithm.
function checkCollision()
{
for (i = 0; i < projectiles.length; i++) {
var p = projectiles[i];
if((p.x + p.width) < ship.x)
{
hit = false;
}
else if(p.x > (ship.x + ship.width))
{
hit = false;
}
else if(p.y > (ship.y + ship.height))
{
hit = false;
}
else if((p.y + p.height) < ship.y)
{
hit = false;
}
else {
console.log(ship.x + ship.width);
}
In the documentation for createjs.Bitmap there do not exist properties for .width and .height. Instead access it's .image (HTMLImageElement) property which have defined width and height properties: ship.image.width and ship.image.height.
If your object is an EaselJS Bitmap, you can retrieve the physical size using getBounds(). This method works for some EaselJS display objects (ie, not Shapes, and accuracy varies with Text).
var bounds = ship.getBounds();
var w = bounds.width;
Notes:
per #Spencer's message, you can access the .image, and get the width/height, but it will be the original size of the image, so if you transform the bitmap instance, or any of the parent containers, the value will be wrong. The getBounds will consider the scale transformation (if it exists)
values may not be correct if the item is rotated.
bounds are based on the registration point, so the x/y might be non-zero.
You will get 0 for width/height if the image is not yet loaded
For your projectile, if it is a shape, the bounds will always be null, but you can manually set them if you know them, and they will be properly calculated/transformed:
var p = new createjs.Shape();
p.graphics.beginFill("red").drawCircle(0,0,20);
p.setBounds(new createjs.Rectangle(-20,-20,40,40));
Here is some info on why there is no .width or .height: http://blog.createjs.com/update-width-height-in-easeljs/

How do I sum the values from an array, one by one, until the total is larger than x?

I have this JS to get the widths of a bunch of sibling elements and add those widths to an array.
var nav = document.querySelector('.js-primary-nav');
var childrenWidths = [];
for(var i=0; i<nav.childElementCount; i++){
childrenWidths.push(nav.children[i].offsetWidth);
}
Now I want to cycle through these widths, adding them up until the total is greater than X (which will be the container width). I then want to get the positions of the values up until the one which tipped the total over into larger than X so I can add those values to one pile and the others to another.
How do I cycle through the values, adding and then comparing the total to a number?
Thanks
You need to define X, but it's a simple loop.
Iterate the nodeList, add the current offsetWidth to the variable tracking, make sure to define the max so you can pass the condition.
When current > max, run whatever code you need to and escape the loop.
var max = [number];
var current = 0;
[].forEach.call(document.querySelector('.js-primary-nav'), function(x) {
current += x.offsetWidth;
if (current >= max) {
//do something
return;
}
});

Resort the array

I have the following, and then I would like like to resort the array to its original state with the out come being posted to console.log. But I'm getting a bit lost on what direction to take:
$(document).ready(function(){
var cards=new Array(
'clu01',
'clu02',
'clu03',
'clu04',
'clu05',
'clu06',
'clu07',
'clu08',
'clu09',
'clu10',
'clu11',
'clu12',
'clu13',
'dia01',
'dia02',
'dia03',
'dia04',
'dia05',
'dia06',
'dia07',
'dia08',
'dia09',
'dia10',
'dia11',
'dia12',
'dia13',
'hea01',
'hea02',
'hea03',
'hea04',
'hea05',
'hea06',
'hea07',
'hea08',
'hea09',
'hea10',
'hea11',
'hea12',
'hea13',
'spa01',
'spa02',
'spa03',
'spa04',
'spa05',
'spa06',
'spa07',
'spa08',
'spa09',
'spa10',
'spa11',
'spa12',
'spa13'
);
function Shuffle(o) {
for(var j, x, i = o.length; i; j = parseInt(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
return o;
};
Shuffle(cards);
console.log(cards);
});
If the original array is sorted alphbetically, you can just do a sort()
cards.sort()
You can also define a custom sorting function:
cards.sort( function(a, b) {
// some logic to compare 2 values to see which goes first
// return 1 if a comes after b
// return -1 if a comes before b
// return 0 if they are the same (this usually means whichever came first will be first)
});
With the time and headache you are going to have in resorting your cards, you might as well have two array, your source "sorted" array, and your shuffled array. When you want to sort your array just use your source array. When you want to shuffle an array you can either reshuffle the "shuffled" array, or make a copy of the source and shuffle that.
The war is already won you can already shuffle cards.
var orig_cards = cards.slice(0);
Shuffle(cards);
console.log(cards);
cards = orig_cards;
console.log(cards);
The way I do things like this is having a separate array of indices, the same length of your array. So, for example for
var cards = ['clu01', 'clu02','clu03'];
you create
var indices = [0, 1, 2];
Now, when you do your shuffle, you compare your original array, but shuffle the indices array. So essentially, you are shuffling the positions in the array but not the elements themselves. That way you keep both separate and can access both states at any time without much overhead.
To access shuffled:
cards[indices[i]];
To access original:
cards[i];

Find the element with the largest area (main content area)?

Given a web page, how do you find the largest rectangle on the webpage which is the main content area?
For example, compare the size of sidebar, header, footer, and main content area. Is it possible to find the main content area by simply searching for the largest rectangle out of all the rectangles discovered on a page?
Usually the tallest and widest rectangle is suspected to be the main content area, wondering if there's an algorithm of some sort in Javascript or Python to test this hypothesis out.
So while the question didn't make much sense to me, I couldn't resist the urge to toy around with the concept of recursively scanning a DOM tree to retrieve and sort elements by their sizeĀ :)
Here's a dumb function for doing so (you can paste it in your browser console):
function scanSizes(root) {
return [].reduce.call(root, function(sizes, node) {
var bounds = node.getBoundingClientRect();
sizes.push({tag: node.outerHTML, area: bounds.width * bounds.height});
var children = node.querySelectorAll("*");
if (children.length > 0)
sizes.push.apply(sizes, scanSizes(children));
return sizes;
}, []).sort(function(x, y) {
var a = x.area, b= y.area;
return a > b ? -1 : a < b ? 1 : 0;
});
}
var sizes = scanSizes(document.querySelectorAll("body > *"));
// sizes[0].tag contains the largest html tag (as a string)
// sizes[0].area its area size in pixels (width * height)
Edit: more seriously, you might be interested in this topic and related answers.
Edit: of course performance wise recursion wasn't a really good idea. You can go with something like this to have a more efficient solution:
function scanSizes(root) {
return [].map.call(root, function(node) {
var bounds = node.getBoundingClientRect();
return {tag: node.outerHTML, area: bounds.width * bounds.height};
}).sort(function(x, y) {
var a = x.area, b= y.area;
return a > b ? -1 : a < b ? 1 : 0;
});
}
var sizes = scanSizes(document.querySelectorAll("*"));
I'm adding another answer because I've just stumbled upon the <main> HTML5 element spec, which developers are supposed to use to define their main contents area, so that's probably the very first element you'll want to check for in any scraped page.
So basically you should check for any single <main> or role="main" element in the page, then only use other contents detection strategiesĀ :)
The current answer is overly complex. The main thing you need to know is element.getBoundingClientRect();. Here's a smaller function - I'm looking for the biggest table but you can use any CSS selector you want.
// Fix NodeList.sort()
NodeList.prototype.sort = Array.prototype.sort
var elements = document.querySelectorAll('table')
var getArea = function(element){
var rectangle = element.getBoundingClientRect();
return rectangle.width * rectangle.height;
}
elements.sort(getArea)[0]

Categories