flex items on left side of overflow flexbox are not shown [duplicate] - javascript

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

Related

Incorrect event target

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.

Change scroll axis on mobile devices (touch)

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>

JS code doesn't act the same when inside a function, and invoked

Example code:
Html:
<div class="wrap">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
<div class="wrap">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
<div class="wrap">
<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>
The css:
*{
margin: 0;
padding: 0;
}
.wrap{
width: 30%;
height: 200px;
background: #f7f7f7;
float: left;
}
.wrap:not(:first-child){
margin-left: 5%;
}
.item{
width: 100%;
height: 47px;
margin-bottom: 3px;
background: #999;
}
The js code that works:
(function($){
$('.wrap').each(function(){
var thisWrap = $(this),
naturalHeight = thisWrap[0].scrollHeight,
restrictedHeight = thisWrap[0].clientHeight;
if (naturalHeight - 5 > restrictedHeight){
thisWrap.css({'background': 'red'});
}
});
})(jQuery);
The js code that doesn't:
(function($){
function myCustomFunction(){
$('.wrap').each(function(){
var thisWrap = $(this),
naturalHeight = thisWrap[0].scrollHeight,
restrictedHeight = thisWrap[0].clientHeight;
if (naturalHeight - 5 > restrictedHeight){
thisWrap.css({'background': 'red'});
}
});
}
$(window).resize(function(){
myCustomFunction();
});
myCustomFunction();
})(jQuery);
In this codepen example both work, but on my test page when the code is in a function and invoked after the definition of the function it doesn't work.
The wrap element is already on the page when the js loads, the js code detects for overflow, that css is defined in a file that gets loaded on line 39, the js gets loaded on line 1300+.
Putting all in a document ready function doesn't help.
How can this code in the same file work properly when not in a function?
EDIT: The issue seems to be with the window resize function, it seems that something is triggering that while the page loads.
This issue was caused by another script which was putting display: none; styling on the wrap-s(so the problem was it fired too late). I solved it by adding a setTimeout with 400ms on the tab change click, and including the function there.
To fix your problem use this
$(window).on('resize', function(){
myCustomFunction();
});

Targeting first element after CSS ordering

Please see this codepen: http://codepen.io/muji/pen/XpEYzO
I need to target the first element after it has been sorted by a css "order" property and give it another CSS property.
Is there some jQuery or something I can use to find the first element after sorting, and then addClass, or something? Many thanks for any help
.wrap {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.featured {
order: 1;
}
.normal {
order: 2;
}
.wrap div {
flex-basis: 50%;
background: #ddd;
border: 2px solid #000;
}
/*this is the part I need help with, as it should only apply to the first element AFTER applying the "order" attribute*/
.wrap div:first-of-type {
flex-basis: 100%;
background: #f00;
}
<h3>wp_query 1 output:</h3>
<div class="wrap">
<div class="normal">Normal</div>
<div class="normal">Normal</div>
<div class="featured">A featured item</div>
<div class="normal">Normal</div>
<div class="featured">A featured item</div>
<div class="normal">Normal</div>
</div>
<h3>wp_query 2 output:</h3>
<div class="wrap">
<div class="normal">Normal</div>
<div class="normal">Normal</div>
<div class="normal">Normal</div>
<div class="normal">Normal</div>
</div>
I'm not aware of any CSS selector for "first in order" or the like (that would be handy, as order becomes more widely-adopted), so it probably will need to be a script-based solution (sadly).
You'd indicated that a solution involving reordering the elements might work for you. If so, that's dead easy. :-) See comments:
// A worker function to tell us the priority of an element (low number = high priority)
function getPriority($el) {
return $el.hasClass("featured") ? 0 : $el.hasClass("normal") ? 1 : 2;
}
// For each .wrap container...
$(".wrap").each(function() {
var $this = $(this);
// Get an array of its child elements
var a = $this.children().get();
// Sort it, putting featured first, then normal, then any others
a.sort(function(a, b) {
return getPriority($(a)) - getPriority($(b));
});
// Append them to the parent, which moves them
$this.append(a);
});
.wrap {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.wrap div {
flex-basis: 50%;
background: #ddd;
border: 2px solid #000;
}
/* Just make the first div the highlighted one */
.wrap div:first-of-type {
flex-basis: 100%;
background: #f00;
}
<h3>wp_query 1 output:</h3>
<div class="wrap">
<div class="normal">Normal</div>
<div class="normal">Normal</div>
<div class="featured">A featured item</div>
<div class="normal">Normal</div>
<div class="featured">A featured item</div>
<div class="normal">Normal</div>
</div>
<h3>wp_query 2 output:</h3>
<div class="wrap">
<div class="normal">Normal</div>
<div class="normal">Normal</div>
<div class="normal">Normal</div>
<div class="normal">Normal</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
You could sort inner divs by position().top and apply style on first one.
$('.wrap').each(function() {
var sort = $(this).find('div').sort(function(a, b) {
return $(a).position().top - $(b).position().top
})
$(sort[0]).addClass('first')
})
.wrap {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.featured {
order: 1;
}
.normal {
order: 2;
}
.wrap div {
flex-basis: 50%;
background: #ddd;
border: 2px solid #000;
}
div.first {
flex-basis: 100%;
background: #f00;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h3>wp_query 1 output:</h3>
<div class="wrap">
<div class="normal">Normal</div>
<div class="normal">Normal</div>
<div class="featured">A featured item</div>
<div class="normal">Normal</div>
<div class="featured">A featured item</div>
<div class="normal">Normal</div>
</div>
<h3>wp_query 2 output:</h3>
<div class="wrap">
<div class="normal">Normal</div>
<div class="normal">Normal</div>
<div class="normal">Normal</div>
<div class="normal">Normal</div>
</div>
An alternative to the position top suggested by #Nenad Vracar would be to actually filter the elements with the smallest order in each group and highlight the first one (in terms of dom order)
$('.wrap').each(function() {
var items = $(this).children(),
minOrder = Infinity,
details = items.map(function(i, el) {
var order = +$(el).css('order');
if (minOrder > order) minOrder = order;
return {
element: el,
order: order
};
}).get(),
firstElement = details.filter(function(item) {
return item.order == minOrder;
})[0];
$(firstElement.element)
.addClass('highlight')
.siblings()
.removeClass('highlight');
});
.wrap {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.featured {
order: 1;
}
.normal {
order: 2;
}
.wrap div {
flex-basis: 50%;
background: #ddd;
border: 2px solid #000;
}
/*this is the part I need help with, as it should only apply to the first element AFTER applying the "order" attribute*/
.wrap .highlight {
flex-basis: 100%;
background: #f00;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h3>wp_query 1 output:</h3>
<div class="wrap">
<div class="normal">Normal</div>
<div class="normal">Normal</div>
<div class="featured">A featured item</div>
<div class="normal">Normal</div>
<div class="featured">A featured item</div>
<div class="normal">Normal</div>
</div>
<h3>wp_query 2 output:</h3>
<div class="wrap">
<div class="normal">Normal</div>
<div class="normal">Normal</div>
<div class="normal">Normal</div>
<div class="normal">Normal</div>
</div>
If you were to alter the order based on media queries you should apply this code on resize.

Flush CSS box alignment

I am practicing CSS with Masonry. I'm doing a bit of basic testing and seeing how everything gets displayed. However I am not sure how to fix this problem. I want to fit all of my pictures inside the .masonry class in my CSS but there is this little bit of space that occurs and I'm not sure how to fix it. I want all the boxes to be flush and no white space to be displayed between each box.
Here is the codepen:
http://codepen.io/anon/pen/Cpedg
my CSS:
* {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
body { font-family: sans-serif; }
.masonry{
margin: 0 auto;
background: #EEE;
max-width: 80%;
}
.masonry .item,
.masonry .grid-sizer {
width: 20%; /*item default width of 20%*/
margin: 0px;
}
.masonry .item,
.masonry .grid-sizer {
height: 60px; /* default hieght of image*/
float: left;
background: #D26;
border: 1px solid #343;
border-color: hsla(0, 0%, 0%, 0.5);
border-radius: 5px;
}
.item.w2 { width: 40%; } /* changes each item to these chracteristics*/
.item.h2 { height: 100px; }
.item.h3 { height: 130px; }
.item.h4 { height: 180px; }
my HTML:
<h1>Masonry - columnWidth</h1>
<div class="masonry js-masonry" data-masonry-options='{ "columnWidth": ".grid-sizer", "itemSelector": ".item" }'>
<div class="grid-sizer"></div>
<div class="item"></div>
<div class="item w2 h2"></div>
<div class="item h3"></div>
<div class="item h2"></div>
<div class="item w3"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item h2"></div>
<div class="item w2 h3"></div>
<div class="item"></div>
<div class="item h2"></div>
<div class="item"></div>
<div class="item w2 h2"></div>
<div class="item w2"></div>
<div class="item"></div>
<div class="item h2"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item h3"></div>
<div class="item h2"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item h2"></div>
</div>
You can remove those spaces between the cells by adding (or rather removing) margin and padding to (from) your body, like so:
body {
font-family: sans-serif;
margin: 0;
padding: 0;
}
That's a possible solution for the upper left arrow, but I'm not quite sure what the problem with the second arrow is?
So it turns out google chrome's zoom acts a bit funky sometimes. quick fix to realign everything is to press ctrl + 0 to realign everything!

Categories

Resources