How can I create a grid system like Flickr's explore page? https://www.flickr.com/explore
I've looked into Isotope (isotope.pkgd.js) but I can't seem to be able to arrange the grid based on the size of the images.
So far this is what I came up with:
HTML:
<h1>Isotope - masonry layout mode</h1>
<div class="isotope">
<div class="item width2 "></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item width2 "></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item width2 "></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item width2 "></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item width2 "></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item width2 "></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item width2 "></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item width2 "></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
CSS:
* {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
body {
font-family: sans-serif;
}
/* ---- isotope ---- */
.isotope {
background: #DDD;
max-width: 1200px;
}
/* clear fix */
.isotope:after {
content: '';
display: block;
clear: both;
}
/* ---- .item ---- */
.item {
float: left;
width: 100px;
height: 100px;
background: #0D8;
border: 2px solid #333;
border-color: hsla(0, 0%, 0%, 0.7);
}
.item.width2 { width: 200px; }
.item.height2 { height: 200px; }
JavaScript:
// external js:
// http://isotope.metafizzy.co/isotope.pkgd.js
$( function() {
$('.isotope').isotope({
itemSelector: '.item',
masonry: {
columnWidth: 100
}
});
});
Im not 100% sure on what you mean with order by image size. But to get the effect of flickr i would try something like this:
layoutMode: 'packery'
$('.isotope').isotope({
itemSelector: '.item',
layoutMode: 'packery',
masonry: {
columnWidth: 100
}
});
Related
4 rows are stored here. Each new row is displayed when the LOAD MORE button is pressed. Each row is displayed as it should and the code works without problems. When the end is reached, a Go button should appear pointing to another page.
What's the best way to do this? I have included the code as an example.
function loadNextSeven() {
let moveItems = $('#off-items-bucket .item').slice(0,7);
moveItems.hide().appendTo('#on-items-bucket').fadeIn('medium');
}
function isThisTheEnd() {
let numberLeft = $('#off-items-bucket .item').length;
if(numberLeft == 0) {
$('#load-more').hide();
}
}
$(document).ready(function() {
loadNextSeven();
isThisTheEnd();
});
$('#load-more').click(function() {
loadNextSeven();
isThisTheEnd();
});
.items-wrapper {
width: 800px;
margin: 0 auto;
}
#off-items-bucket {
display: none; /* REALLY THE ONLY LINE YOU NEED*/
background-color: palegreen;
box-shadow: 0 0 0 5px #000;
float:left;
box-sizing: border-box;
margin: 20px 0;
width:100%;
}
.item {
width: 100px;
height: 100px;
float:left;
margin: 5px;
}
.green {
background-color: green;
}
.red {
background-color: red;
}
.blue {
background-color: blue;
}
.yellow {
background-color: yellow;
}
button {
width:200px;
margin: 0 auto;
padding:10px;
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="items-wrapper">
<div id="on-items-bucket"></div>
<div id="off-items-bucket">
<!-- put the items in the bucket -->
<div class="item red"></div>
<div class="item red"></div>
<div class="item red"></div>
<div class="item red"></div>
<div class="item red"></div>
<div class="item red"></div>
<div class="item red"></div>
<div class="item yellow"></div>
<div class="item yellow"></div>
<div class="item yellow"></div>
<div class="item yellow"></div>
<div class="item yellow"></div>
<div class="item yellow"></div>
<div class="item yellow"></div>
<div class="item blue"></div>
<div class="item blue"></div>
<div class="item blue"></div>
<div class="item blue"></div>
<div class="item blue"></div>
<div class="item blue"></div>
<div class="item blue"></div>
<div class="item green"></div>
<div class="item green"></div>
<div class="item green"></div>
<div class="item green"></div>
<div class="item green"></div>
<div class="item green"></div>
<div class="item green"></div>
</div>
<button id="load-more">LOAD MORE</button>
</div>
One way is to just rename the button when you get to the end. Then you can just test it in the $('#load-more').click(function() function.
First, move the numberLeft variable out of it's function so that other functions can access it. When numberLeft === 0 just rename the button with $('#load-more').text("GO ->")
let numberLeft
function isThisTheEnd() {
numberLeft = $('#off-items-bucket .item').length;
if (numberLeft === 0) {
$('#load-more').text("GO ->");
}
}
In this function, just test for numberLeft and react accordingly.
$('#load-more').click(function() {
if (numberLeft === 0) {
alert("Go to next page")
} else {
loadNextSeven();
isThisTheEnd();
}
});
function loadNextSeven() {
let moveItems = $('#off-items-bucket .item').slice(0, 7);
moveItems.hide().appendTo('#on-items-bucket').fadeIn('medium');
}
let numberLeft
function isThisTheEnd() {
numberLeft = $('#off-items-bucket .item').length;
if (numberLeft == 0) {
$('#load-more').text("GO ->");
}
}
$(document).ready(function() {
loadNextSeven();
isThisTheEnd();
});
$('#load-more').click(function() {
if (numberLeft === 0) {
alert("Go to next page")
} else {
loadNextSeven();
isThisTheEnd();
}
});
.items-wrapper {
width: 800px;
margin: 0 auto;
}
#off-items-bucket {
display: none;
/* REALLY THE ONLY LINE YOU NEED*/
background-color: palegreen;
box-shadow: 0 0 0 5px #000;
float: left;
box-sizing: border-box;
margin: 20px 0;
width: 100%;
}
.item {
width: 100px;
height: 100px;
float: left;
margin: 5px;
}
.green {
background-color: green;
}
.red {
background-color: red;
}
.blue {
background-color: blue;
}
.yellow {
background-color: yellow;
}
button {
width: 200px;
margin: 0 auto;
padding: 10px;
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="items-wrapper">
<div id="on-items-bucket"></div>
<div id="off-items-bucket">
<!-- put the items in the bucket -->
<div class="item red"></div>
<div class="item red"></div>
<div class="item red"></div>
<div class="item red"></div>
<div class="item red"></div>
<div class="item red"></div>
<div class="item red"></div>
<div class="item yellow"></div>
<div class="item yellow"></div>
<div class="item yellow"></div>
<div class="item yellow"></div>
<div class="item yellow"></div>
<div class="item yellow"></div>
<div class="item yellow"></div>
<div class="item blue"></div>
<div class="item blue"></div>
<div class="item blue"></div>
<div class="item blue"></div>
<div class="item blue"></div>
<div class="item blue"></div>
<div class="item blue"></div>
<div class="item green"></div>
<div class="item green"></div>
<div class="item green"></div>
<div class="item green"></div>
<div class="item green"></div>
<div class="item green"></div>
<div class="item green"></div>
</div>
<button id="load-more">LOAD MORE</button>
</div>
This question already has answers here:
Can't scroll to top of flex item that is overflowing container
(12 answers)
Closed 1 year ago.
I have a column flexbox with width:100% and overflow-x:auto , inside this flexbox there are variable numbers of rows and each row can contain 1,2,4,8,16,... elements.
consider bellow example:
first row ---> has 1 element
second row ---> has 2 elements
third row ---> has 4 elements
forth row ---> has 8 elements
...
and elements on each row should be centered.
in my code all the left side elements will be cut off , how can I prevent this ??
const itemWidth = 300; //300px
document.querySelectorAll('.item').forEach((item) => {
item.style.width = `${itemWidth}px`;
});
document.querySelectorAll('.row').forEach((row, i) => {
row.style.width = `${
(Math.pow(2, i) + (Math.pow(2, i) - 1)) * itemWidth
}px`; //calc needed width of each row
});
.rows {
width: 100%;
overflow-x: auto;
display: flex;
flex-direction: column;
align-items: center;
}
.row {
display: flex;
justify-content: space-between;
margin-top: 20px;
}
.item {
height: 100px;
background-color: royalblue;
margin: 20px;
}
<div class="rows">
<div class="row">
<div class="item"></div>
</div>
<div class="row">
<div class="item"></div>
<div class="item"></div>
</div>
<div class="row">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
<div class="row">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
</div>
For the first div. You are assigning a width of only one item. So, flex items got cut off to left. Please check the below code. Get the length of items contains a max length of a row. Assign it to all the divs. If your unable to view the below code in snippet. Check it here - https://codepen.io/kcsmastermind/pen/MWpPBrY
const itemWidth = 300; //300px
document.querySelectorAll('.item').forEach((item) => {
item.style.width = `${itemWidth}px`;
});
let rows = document.getElementsByClassName("row");
let maxNumber = 0;
for (let i=0; i < rows.length; i++) {
if (maxNumber < rows[i].childElementCount) maxNumber = rows[i].childElementCount;
};
document.querySelectorAll('.row').forEach((row, i) => {
row.style.minWidth = `${
maxNumber * itemWidth
}px`; //calc needed width of each row
});
.rows {
display: flex;
flex-direction: column;
align-content: center;
width: 100%;
overflow-x: auto;
}
.row {
display: flex;
justify-content: center;
margin-top: 20px;
}
.item {
height: 100px;
background-color: royalblue;
margin: 20px;
}
<div class="rows">
<div class="row">
<div class="item"></div>
</div>
<div class="row">
<div class="item"></div>
<div class="item"></div>
</div>
<div class="row">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
<div class="row">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
</div>
Here's a pure CSS solution (I took some liberties with styling to hopefully make it easier to see what's going on):
https://jsfiddle.net/5hq924uf/1/
Rather than calculating element widths via Javascript, the .rows container width is set based on its child content with this rule:
width: min-content;
This then props the page open, preventing elements from getting cut off when centered using flex.
Note that while the min-content feature seems to be well-supported in modern browsers, it's not going to work in some legacy browsers, so be aware:
https://caniuse.com/mdn-css_properties_width_min-content
I'm creating a selector plugin (which can set active class to the multiple elements).
Algorithm:
onMouseDown create element;
onMouseMove while onMouseDown move to elements I want to select, my created element changes size.
onMouseUp get event target and do magic.
This is the short version due to shortness but I have a problem with getting proper event target. When I move to the right and bottom/top (my x axis gets positive), I'm getting the proper event target. But when my x-axis is negative I always getting e.target the element which changes size itself, not the elements I need.
const container = document.querySelector('.container');
let selector = null;
container.addEventListener('mousedown', (e) => {
selector = document.createElement('div');
selector.style.position = 'fixed';
selector.style.border = '1px dashed blue';
selector.style.width = '1px';
selector.style.height = '1px';
selector.style.left = e.clientX+'px'
selector.style.top = e.clientY+'px'
selector.className = "selector";
container.appendChild(selector);
const mouseDownClientX = e.clientX;
const mouseDownClientY = e.clientY;
document.onmousemove = e => {
renderselector(e, mouseDownClientX, mouseDownClientY);
}
document.onmouseup = e => {
console.log(e.target);
selector.remove();
}
});
function renderselector(e, mouseDownClientX, mouseDownClientY) {
const top = Math.min(e.clientY, mouseDownClientY);
const left = Math.min(e.clientX, mouseDownClientX)
const width = Math.abs(e.clientX - mouseDownClientX);
const height = Math.abs(e.clientY - mouseDownClientY);
selector.style.width = width + 'px';
selector.style.height = height + 'px';
selector.style.top = top+'px';
selector.style.left = left+'px';
}
* {
padding: 0px;
margin: 0px;
box-sizing: border-box;
}
.container {
display: flex;
justify-content: center;
align-content: center;
flex-wrap: wrap;
width: 50vw;
height: 100vh;
margin: 0 auto;
border: 1px solid gray;
}
.item {
padding: 20px;
flex: 0 0 50px;
border: 1px solid gray;
}
<div class='container'>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
Needed to change z-index of my selector to lesser than my divs.
I have a container that can be scrolled through horizontally. This was done by rotating the container by 90 degrees.
This is however only a visual workaround: the visual horizontal scrolling is done by physical vertical scrolling. On mobile devices this is very counter-intuitive.
Is there any way to manipulate the scrolling behaviour so that the container can be scrolled through with horizontal touch gestures?
Here is a simplified demonstration of the container:
.item {
background: black;
height: 50px;
width: 50px;
margin: 5px;
}
.container {
background: yellow;
width: fit-content;
height: 300px;
overflow-y: scroll;
transform: rotate(-90deg);
transform-origin: top right;
}
<div class="container">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
Instead of using transform, actualy place the elements side by side in a flex container.
Because you don't want the elements to shrink with the container size you a can set a min-width on the items:
.item {
background: black;
height: 50px;
width: 50px;
margin: 5px;
min-width:50px;
}
.container {
display:flex;
background: yellow;
width: 300px;
overflow-x: scroll;
}
<div class="container">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
I think I'm probably doing something wrong that's incredibly obvious...
This is my HTML
<div id="container">
<div class="item"></div>
<div class="item w2"></div>
<div class="item"></div>
<div class="item w2"></div>
<div class="item h2"></div>
<div class="item"></div>
<div class="item h2"></div>
<div class="item w2 h2"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item h2"></div>
<div class="item"></div>
</div>
I'm calling jQuery 1.7.1 and modernizr in the head, and calling masonry.js and the below script before the /body
$(document).ready(function() {
$('#container').masonry({
columnWidth: 440,
itemSelector: '.item',
isFitWidth: false,
transitionDuration: 0,
});
});
Incidentally my CSS is below if that makes any difference
#container {padding: 5px;}
.item {
width: 220px;
height: 160px;
float: left;
margin: 5px;
background: #CCC;
}
.item.w2 {width: 440px;}
.item.h2 { height: 280px;}
No console errors but doesn't seem to be stacking how I would imagine
** see here - http://www.rsg-media.com/masonry/test.html
I would think it would appear a bit more like this
http://www.code-pal.com/wp-content/uploads/2013/01/masonry-css3-jquery-fallback.jpg
...or am I being an idiot?
done a snippet here, changed the width of w2 to a percentage and it moved them around a bit better... I've reset it to original now but I think there's your problem
$(document).ready(function() {
$('#container').masonry({
columnWidth: 440,
itemSelector: '.item',
isFitWidth: false,
transitionDuration: 0,
});
});
#container {padding: 5px;}
.item {
width: 220px;
height: 160px;
float: left;
margin: 5px;
background: #CCC;
}
.item.w2 {width: 440px;}
.item.h2 { height: 280px;}
<script src="http://desandro.github.io/masonry/jquery.masonry.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="container">
<div class="item"></div>
<div class="item w2"></div>
<div class="item"></div>
<div class="item w2"></div>
<div class="item h2"></div>
<div class="item"></div>
<div class="item h2"></div>
<div class="item w2 h2"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item h2"></div>
<div class="item"></div>
</div>