Targeting first element after CSS ordering - javascript

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.

Related

CSS/HTML how to show only the first two lines of a flexbox

For a project I need to create a search bar. The search results need to be displayed in a grid. The items should be next to each other and two rows high. If there are more results I need to show a button +32 to display all the results.
The problem I am facing is that I have no idea how to accomplish this. I have used flexbox and flex-wrap in order to get new items to be shown in the next line when they don't fit on the previous one. But I don't know how to only display the first two lines.
.container{
display: flex;
flex-wrap: wrap;
background-color: blue;
max-width: 15rem;
gap: 1rem;
}
.item{
background-color: red;
padding: 0 0.2rem;
}
<div class="container">
<div class="item">
<p>Hello</p>
</div>
<div class="item">
<p>Helllooo</p>
</div>
<div class="item">
<p>Hel</p>
</div>
<div class="item">
<p>Hello</p>
</div>
<div class="item">
<p>Test</p>
</div>
<div class="item">
<p>For</p>
</div>
<div class="item">
<p>Hello</p>
</div>
<div class="item">
<p>Hello</p>
</div>
<div class="item">
<p>Helllooo</p>
</div>
<div class="item">
<p>Hel</p>
</div>
<div class="item">
<p>Hello</p>
</div>
</div>
Here is a quick solution, although it doesn't calculate the length yet.
items = ["Helooo","Testing","Holaaaa","Hiiii","Hello","Greetings fine sir","Welcome","Bonjour","Oi you there"];
let limit = 5; // You will need to calculate this somehow
function display(limit){
document.getElementById("searchContainer").innerHTML = "";
for (let item of items.slice(0,limit)){
document.getElementById("searchContainer").innerHTML += `<div class=item>${item}</div>`;
}
if (items.length > limit){
document.getElementById("searchContainer").innerHTML += `<div class=item onclick="display(${items.length})">+${items.length-limit}</div>`;
}
}
display(limit);
.container{
display: flex;
flex-wrap: wrap;
background-color: whitesmoke;
max-width: 15rem;
gap: 1rem;
}
.item{
background-color: rgb(100,100,200);
padding: 0 0.2rem;
border-radius: 2px;
}
<div class=container id=searchContainer>
</div>

Add class to div on scroll up and down

As you can see I have a number of dots lined vertically! On scrolling down it works, as one scrolls the next dot gets a class thats chnages the style of the ball, I want the exact same to happen when i scroll up but it is not working! Please any input is appreciated!
Here is a code pen for your reference!
below the html, css and javascript:
var activeMilestone = function() {
var milestoneBalls = $('.dot');
milestoneBalls[0].classList.add("active");
window.onscroll = function() {
milestoneBalls.each(function(i, v) {
var thisBall = $(this);
var nextBall = milestoneBalls[i + 1];
var prevBall = milestoneBalls[i - 1];
var thisPositionTop = thisBall.offset().top + ($(this).parent().height() / 3);
var winScroll = window.scrollY;
if (thisPositionTop <= winScroll) {
nextBall.classList.add("active");
thisBall.addClass("inactive");
}
if (thisPositionTop >= winScroll) {
//this.classList.add("inactive_ball");
}
});
}
}
$(document).ready(activeMilestone);
.wrapper {
height: 1000px;
background-color: wheat;
}
.info_wrapper {
margin-top: 70px;
}
.container {
height: 50%;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
}
.dot {
width: 30px;
height: 30px;
border-radius: 20px;
background-color: maroon;
border: solid 4px green;
}
.active {
border: solid 4px yellow;
background-color: red;
}
.inactive {
background-color: maroon;
border: solid 4px green;
}
.text {}
.header {
width: 100%;
height: 50px;
background-color: lightblue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="wrapper">
<div class="header"></div>
<div class="container">
<div class="info_wrapper">
<div class="text"></div>
<div class="dot "></div>
</div>
<div class="info_wrapper">
<div class="text"></div>
<div class="dot "></div>
</div>
<div class="info_wrapper">
<div class="text"></div>
<div class="dot "></div>
</div>
<div class="info_wrapper">
<div class="text"></div>
<div class="dot"></div>
</div>
<div class="info_wrapper">
<div class="text"></div>
<div class="dot"></div>
</div>
<div class="info_wrapper">
<div class="text"></div>
<div class="dot"></div>
</div>
<div class="info_wrapper">
<div class="text"></div>
<div class="dot"></div>
</div>
<div class="info_wrapper">
<div class="text"></div>
<div class="dot"></div>
</div>
</div>
</div>
So this logic performs the same that you were doing already. With the added logic of, if the element should not advance, and we are not the first element, we check to see if the previous ball should be active. And if so, we make it active.
var activeMilestone = function() {
var milestoneBalls = $('.dot');
milestoneBalls.eq(0).addClass('active');
window.addEventListener('scroll', function() {
var activeBall = milestoneBalls.filter('.active');
var activeBallIndex = milestoneBalls.index( activeBall );
var activeBallPositionTop = activeBall.offset().top + (activeBall.parent().height() / 3);
if (activeBallPositionTop <= window.scrollY) {
activeBall.removeClass('active');
milestoneBalls.eq( activeBallIndex + 1).addClass('active');
} else if ( activeBallIndex ) {
var previousBall = milestoneBalls.eq( activeBallIndex - 1 );
var previousBallPositionTop = previousBall.offset().top + (previousBall.parent().height() / 3);
if (previousBallPositionTop > window.scrollY) {
activeBall.removeClass('active');
previousBall.addClass('active');
}
}
});
}
$(document).ready(activeMilestone);
.wrapper {
height: 1000px;
background-color: wheat;
}
.info_wrapper {
margin-top: 70px;
}
.container {
height: 50%;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
}
.dot {
width: 30px;
height: 30px;
border-radius: 20px;
background-color: maroon;
border: solid 4px green;
}
.active {
border: solid 4px yellow;
background-color: red;
}
.inactive {
background-color: maroon;
border: solid 4px green;
}
.text {}
.header {
width: 100%;
height: 50px;
background-color: lightblue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="wrapper">
<div class="header"></div>
<div class="container">
<div class="info_wrapper">
<div class="text"></div>
<div class="dot "></div>
</div>
<div class="info_wrapper">
<div class="text"></div>
<div class="dot "></div>
</div>
<div class="info_wrapper">
<div class="text"></div>
<div class="dot "></div>
</div>
<div class="info_wrapper">
<div class="text"></div>
<div class="dot"></div>
</div>
<div class="info_wrapper">
<div class="text"></div>
<div class="dot"></div>
</div>
<div class="info_wrapper">
<div class="text"></div>
<div class="dot"></div>
</div>
<div class="info_wrapper">
<div class="text"></div>
<div class="dot"></div>
</div>
<div class="info_wrapper">
<div class="text"></div>
<div class="dot"></div>
</div>
</div>
</div>
You can use on wheel from jQuery.
$(window).on('wheel',function(e) {
var mov = e.originalEvent.deltaY;
if(mov > 0) {
alert("up");
}else {
alert("down");
}
});
This will sort out your problem regarding detection of scrolling direction.

scrollreveal.js and flexbox

I'm experiencing an issue with using scrollreveal.js together with flexbox.
I've created a few two-column rows along the page using display flex and when trying to reveal each column separately with the scrollreveal reference only one of them are working.
Any workaround while still being able to maintain the flex attributes?
HTML
<div class="grid">
<div class="column __50 __reveal">one</div>
<div class="column __50 __reveal">two</div>
</div>
CSS
.grid {
display: flex;
}
.column.__50 {
width: 50%;
}
JS
window.sr = ScrollReveal({
distance: '30px',
duration: 1000,
scale: 0
});
sr.reveal('.__reveal');
If I understand you clearly:
Given- the existing code
When- page is scrolled
Then- Columns should scroll into view
I've provided a snippet that shows that it does work based on your code but you have to give enough space for the scrollreveal effect to take place.
TO TEST
Add a div that takes up the space from the initial view.
Add a height to the .column.__50 class so you can see it in action.
Add a border so you can see how they are placed, you can always comment the border out after you no longer need it. Good practice for 'debugging' and visualizing your divs.
window.sr = ScrollReveal({
distance: '30px',
duration: 1000,
scale: 0
});
sr.reveal('.__reveal');
.grid {
display: flex;
}
.column.__50 {
width: 50%;
height: 800px;
border: 2px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/scrollReveal.js/4.0.0-beta.25/scrollreveal.js"></script>
<div class="grid">
<div class="column __50 ">
No class="__reveal 1"
</div>
<div class="column __50 ">
No class="__reveal 2"
</div>
</div>
<div class="grid">
<div class="column __50 __reveal">
one
</div>
<div class="column __50 __reveal">
two
</div>
</div>
As I understand, You have to pass a sequence interval (in milliseconds) to the reveal() method, making sequenced animations a breeze.
sr.reveal('.__reveal', 300);
You can read the documentation from this reference link
Stack Snippet
window.sr = ScrollReveal({
distance: '30px',
duration: 1000,
scale: 0
});
sr.reveal('.__reveal', 300);
.grid {
display: flex;
font: 13px Verdana;
flex-wrap: wrap;
}
.column.__50 {
width: 50%;
height: 100px;
background: red;
border: 8px solid #ffffff;
border-radius: 20px;
padding: 20px;
color: #fff;
box-sizing: border-box;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://unpkg.com/scrollreveal/dist/scrollreveal.min.js"></script>
<div class="grid">
<div class="column __50 __reveal">one</div>
<div class="column __50 __reveal">two</div>
<div class="column __50 __reveal">three</div>
<div class="column __50 __reveal">four</div>
</div>

How to get actual width of a block displayed with flex-direction:column properly?

For Example:
HTML
console.log($('.container').width()); // 600px as we defined in css.
console.log($('.list').width()); // 600px
console.log($('.box').eq('-1').position().left + $('.box').outerWidth());
body{padding:0;margin:0;}
.container {
width: 600px;
backround:#E1BEE7;
}
.list {
display: flex;
flex-direction: column;
flex-wrap: wrap;
height:120px;
background:#FFCDD2;
}
.box {
margin:10px;
flex-shrink:0;
flex-grow:0;
flex-basis:auto;
width: 100px;
height: 100px;
text-align:center;
line-height:100px;
background: #F44336;
color:#FFF;
font-size:2rem;
}
.result{
padding:1rem;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="container">
<div class="list">
<div class="box">1</div>
<div class="box">2</div>
<div class="box">3</div>
<div class="box">4</div>
<div class="box">5</div>
<div class="box">6</div>
<div class="box">7</div>
<div class="box">8</div>
<div class="box">9</div>
<div class="box">10</div>
</div>
</div>
There is 10 boxes, and each box with 100px width, the actual width of element .list should be 1000px not 600px.
I tried to use the following method to get actual width of list: The left position of last child element and its outerWidth
console.log($('.box').eq('-1').position().left + $('.box').outerWidth());
What I want to know: is there a better method to get actual width of this kind of flexbox element? some better built-in javascript or jquery method exists for this?
What I need is just el.scrollWidth.
Probably more code than you wanted, but you could just sum the width of the elements inside the div, using javascript with jQuery like this:
var totalWidth = 0;
$('.box').each(function() {
totalWidth += $(this).width();
});
$('#msg').html(
"<p>totalWidth: " + totalWidth + "</p>"
);
with a live example: https://jsfiddle.net/yeeqkd2e/4/
or without jQuery
var totalWidth = 0;
var cont = document.getElementsByClassName('box');
for (var i = 0, len = cont.length; i < len; i++) {
totalWidth += cont[i].offsetWidth;
}
document.getElementById('msg').innerHTML = "<p>totalWidth: " + totalWidth + "</p>";
Native Javascript: https://jsfiddle.net/yeeqkd2e/6/
As you've discovered, scrollWidth will provide you the dimension you're looking for. The reason that outerWidth isn't giving you the total width of the internal elements is that your .list element is actually 600px wide. The additional content is overflowing its parent, allowing it to display despite being outside of the .list.
You can test this by changing the overflow property to hidden on .list:
body{ padding:0; margin:0; }
.container {
width: 600px;
}
.list {
display: flex;
flex-direction: column;
flex-wrap: wrap;
height: 120px;
background: #FFCDD2;
overflow: hidden;
}
.box {
margin:10px;
flex-shrink:0;
flex-grow:0;
flex-basis:auto;
width: 100px;
height: 100px;
text-align:center;
line-height:100px;
background: #F44336;
color:#FFF;
font-size:2rem;
}
.result{
padding:1rem;
}
<div class="container">
<div class="list">
<div class="box">1</div>
<div class="box">2</div>
<div class="box">3</div>
<div class="box">4</div>
<div class="box">5</div>
<div class="box">6</div>
<div class="box">7</div>
<div class="box">8</div>
<div class="box">9</div>
<div class="box">10</div>
</div>
</div>
Note that when you run this, the .list element clips the remaining boxes.
Hopefully this helps you understand what is going on in addition to solving the problem for you. Cheers!

JS: Iterate through divs

I have 5 div elements, all with class='item'.
Im catching them with: var x = document.getElementsByClassName("item");
Now I want to make disappear that div, which was mouseovered.
https://jsfiddle.net/LqsLbrco/1/
But it doesn't work as it supposed to do. Because all elements are disappearing, not only this which was hovered.
Edit: My point is that the modal div appear (the pink box) when the item div is hovered. Check out the new jsfiddle: https://jsfiddle.net/LqsLbrco/10/
There's a div behind the blue boxes, I want him to appear when the user hovers the blue box.
If you do it in jQuery, you could just do this.
Modified the markup to accommodate the requirements.
$(function() {
$(".container .item").bind("mouseover", function(event) {
$(event.target).find(".modal").show();
});
$(".container .modal").bind("mouseleave", function(event) {
$(event.target).hide();
})
});
.item {
height: 100px;
width: 100px;
background-color: blue;
display: inline-block;
margin: 5px;
}
.container {
display: inline-block;
}
.modal {
height: 100px;
width: 100px;
background-color: pink;
display: inline-block;
margin: 0px;
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<div class="item">
<div class="modal"></div>
</div>
</div>
<div class="container">
<div class="item">
<div class="modal"></div>
</div>
</div>
<div class="container">
<div class="item">
<div class="modal"></div>
</div>
</div>
<div class="container">
<div class="item">
<div class="modal"></div>
</div>
</div>
<div class="container">
<div class="item">
<div class="modal"></div>
</div>

Categories

Resources