I've been stuck trying to figure out how to code a menu much like what you see on the Playstation website: http://us.playstation.com/
EDIT: Fiddle: http://jsfiddle.net/jjcarlson/7q64A/
So far I have a number of issues. The first is that I have been unable to create the 100% width, because, I am assuming, of the parent/child relationship.
The second issue I have is that my Timeout works on all class elements rather than only the currently hovered element. In other words, if all elements have slid down and one is hovered over, they all will remain open until none of them have been hovered for 1.5 seconds. I admit that my inability to come up with a solution may be due to my limited experience with the language. Below is the CSS:
.accordion-container {
width: 90%;
padding-bottom: 5px;
margin: 20px 0 0 20px;
}
.accordion {
width: 40%;
padding: 20px;
margin: 0 15px 35px;
position: relative;
float: left;
display: inline-block;
}
.accordion-question {
margin: 0;
padding: 0 0 5px 20px;
display: inline-block;
color: #06F;
background-color: #9F0;
cursor: pointer;
}
.accordion-answer-container {
padding: 0 20px;
overflow: hidden;
color: #999;
background: #F00;
}
.accordion-answer {
margin: 0;
padding: 0;
color: #0C0;
}
Then, the JQuery:
$(document).ready(function () {
var menu = $('.accordion-answer')
var timeout = 0;
var hovering = false;
menu.hide();
$('.accordion-question').hover(function () {
hovering = true;
// Open the menu
$(this).closest('.accordion').find('.accordion-answer')
.stop(true, true)
.delay(400).slideDown(600);
if (timeout > 0) {
clearTimeout(timeout);
}
})
.on("mouseleave", function () {
resetHover();
});
$('.accordion-answer').hover(function () {
hovering = true;
startTimeout();
})
.on("mouseleave", function () {
resetHover();
});
function startTimeout() {
timeout = setTimeout(function () {
closeMenu();
}, 1500);
};
function closeMenu() {
if (!hovering) {
$('.accordion-answer').stop(true, true).slideUp(400);
}
};
function resetHover() {
hovering = false;
startTimeout();
};
});
And finally, the HTML:
<div class="accordion-container">
<div class="accordion">
<div class="accordion-question">
<h2>Is this a question?</h2>
</div>
<div class="accordion-answer-container">
<div class="accordion-answer">
<p>To be honest, I am not sure</p>
<ul>
<li>List item one</li>
<li>List item two</li>
</ul>
<p>That is all.</p>
</div>
</div>
</div><!-- /accordion -->
<div class="accordion" id="testanchor">
<div class="accordion-question">
<h2>What would be a good second question?</h2>
</div>
<div class="accordion-answer-container">
<div class="accordion-answer">
<p>I don’t know, man!</p>
<p>That is all.</p>
</div>
</div>
</div><!-- /accordion -->
</div>
Styling is minimal right now (sorry) as I'm just trying to get this figured out. Thank you for any help you can provide.
To get a width of 100% you should not display them as inline-block and set the width of .accordion to 100%.
In the hover-event you set hovering to true. If the next hover-event occurs prior to the call of closeMenu, then the if clause will already be false.
You should be able to accomplish the 100% width of your dropdown by altering the css of your .according-answer-container to a fixed position along with setting left and right to 0:
.accordion-answer-container {
padding: 0 20px;
overflow: hidden;
color: #999;
background: #F00;
position: fixed;
left: 0;
right: 0;
}
An update to your fiddle shows this working
Related
I'm currently developing an e-catalogue. Following the suggestion of a Stackoverflow contributor, I now have a navigation bar with a dropdown that produces a two-container menu. Next, I am trying to set it in such a way that when one hovers over a specific element in one container (the left container), the contents of the other container (the right one) of the dropdown, change and allows the user, interact with the changed contents of the right container.
I am using 'mouseenter' and 'mouseleave' event handlers, and yes, you've guessed it, the moment I leave the element (in the left container) I hovered over to trigger the change (in the right container), the contents of the right container reverse back to their original contents!
To get 'round' this problem, within the mouseenter eventlistener function, I wrote in a second mouseenter eventlistener function, this time for the right container itself. I hoped that if I left the element (in the left container), that triggered the change in the right container, and went into the right container, I would be able to interact with the altered contents of the right container. Alas, it worked it!
The problem now, however, is that ANYTIME I hover over the right container, the contents change as if I had hovered over the specific element in the left container, regardless of whether or not I had actually hovered over that element in the left container.
I have tried numerous approaches, including, 'mouseout' (which 'bubbles'), and also tried giving the two mouseenter event functions, names, so that the 'inner' function for the mouseenter event of the right container only executes when the 'outer' function for the mouseenter event of the element in the left container has executed (a very Pythonistic way of thinking!), but nothing has worked.
I need to keep a mouseout or mouseleave event of some sort for the element in the left container; otherwise, the change in the right container will persist as you move the mouse on to other elements in the left container.
Ultimately, I want each element in the left container to trigger different changes in the contents of the right container, much like what you see in the dropdown here.
A minimal working version of my code is shown below:
// block-1h selectors
const breakingLine = document.querySelector(".breakingline");
const breaking = document.querySelector(".breaking");
// block-2h selector(s)
const block2H = document.querySelector(".block-2h");
const drop2HCaptionText = document.querySelector(".drop-2h-captiontext");
// Event listeners
breakingLine.addEventListener("mouseenter", function() {
drop2HCaptionText.textContent = "Camon C2000 Rotavator";
block2H.addEventListener("mouseenter", function() {
drop2HCaptionText.textContent = "Camon C2000 Rotavator";
})
})
breaking.addEventListener("mouseleave", function() {
drop2HCaptionText.textContent = "Boss Ladderspan 3T Scaffold Tower (Single Width)";
block2H.addEventListener("mouseleave", function() {
drop2HCaptionText.textContent = "Boss Ladderspan 3T Scaffold Tower (Single Width)";
})
})
.nav-list {
list-style: none;
border: 1px solid blue;
display: flex;
flex-flow: row wrap;
align-items: left;
justify-content: left;
color: white;
background-color: #429CD9;
}
#hire-dropdown {
position: absolute;
cursor: pointer;
padding-right: 3em;
padding-left: 3em;
}
.hdrop,
.block-1h,
.block-2h {
display: none;
}
#hire-dropdown:hover * {
display: grid;
}
#hire-dropdown .hdrop {
grid-template-areas: "block-1h block-2h";
grid-template-columns: 1fr 1fr;
}
.block-1h {
grid-area: "block-1h";
height: 30em;
}
.block-2h {
grid-area: "block-2h";
background-color: white;
border: 1px solid black;
height: 40em;
}
.drop-1h-list {
list-style: none;
display: block;
border: 1px solid black;
background-color: white;
height: 40em;
}
.drop-most-popular-hire,
.drop-2h-captiontext {
color: #3D3F41;
}
.drop-most-popular-hire {
padding-left: 3em;
}
<nav>
<ul class="nav-list">
<li>Nothing</li>
<li class="to-hire">
<div id="hire-dropdown">To Hire
<div class="hdrop">
<div class="block-1h">
<ul class="drop-1h-list">
<li>Access</li>
<li class="breakingline"><a class="breaking" href="#">Breaking</a></li>
<li class="compactionline"><a class="compaction" href="#">Compaction</a></li>
</ul>
</div>
<div class="block-2h">
<h3 class="drop-most-popular-hire">Our most popular product in this category</h3>
<p class="drop-2h-captiontext">Boss Ladderspan 3T Scaffold Tower (Single Width)</p>
</div>
</div>
</div>
</li>
</ul>
</nav>
Your (constructive) help will be most appreciated.
You could just remove the mouseleave event listener.
When you enter a li element in the left container, the mouseenter event listener will be fired and the text in the right container will be changed.
The content will be changed to another text only when you enter another element in the left container.
const drop2HCaptionText = document.querySelector(".drop-2h-captiontext");
let texts = [
"Boss Ladderspan 3T Scaffold Tower (Single Width)",
"Camon C2000 Rotavator",
"Boss Ladderspan 3T Scaffold Tower (Single Width)",
]
let listItems = document.querySelectorAll('.drop-1h-list li');
listItems.forEach((item, index) => {
item.addEventListener('mouseenter', () => {
drop2HCaptionText.textContent = texts[index];
})
})
.nav-list {
list-style: none;
border: 1px solid blue;
display: flex;
flex-flow: row wrap;
align-items: left;
justify-content: left;
color: white;
background-color: #429CD9;
}
#hire-dropdown {
position: absolute;
cursor: pointer;
padding-right: 3em;
padding-left: 3em;
}
.hdrop,
.block-1h,
.block-2h {
display: none;
}
#hire-dropdown:hover * {
display: grid;
}
#hire-dropdown .hdrop {
grid-template-areas: "block-1h block-2h";
grid-template-columns: 1fr 1fr;
}
.block-1h {
grid-area: "block-1h";
height: 30em;
}
.block-2h {
grid-area: "block-2h";
background-color: white;
border: 1px solid black;
height: 40em;
}
.drop-1h-list {
list-style: none;
display: block;
border: 1px solid black;
background-color: white;
height: 40em;
}
.drop-most-popular-hire,
.drop-2h-captiontext {
color: #3D3F41;
}
.drop-most-popular-hire {
padding-left: 3em;
}
<nav>
<ul class="nav-list">
<li>Nothing</li>
<li class="to-hire">
<div id="hire-dropdown">To Hire
<div class="hdrop">
<div class="block-1h">
<ul class="drop-1h-list">
<li>Access</li>
<li class="breakingline"><a class="breaking" href="#">Breaking</a></li>
<li class="compactionline"><a class="compaction" href="#">Compaction</a></li>
</ul>
</div>
<div class="block-2h">
<h3 class="drop-most-popular-hire">Our most popular product in this category</h3>
<p class="drop-2h-captiontext">Boss Ladderspan 3T Scaffold Tower (Single Width)</p>
</div>
</div>
</div>
</li>
</ul>
</nav>
I'm new to jquery. I'm trying to write a script that will hide the div "box" and all children. When the user scrolls to the bottom of the page, the div "box" and all children display. For time's sake, we'll say the children are "chamber1", "chamber2" and "chamber 3".
when I hide "box", it only removes that div.
$(document).ready(function() {
$("#box").hide();
});
Apologies for lack of code, but I'm having trouble understanding this lesson and I can't find an exact example of what I'm trying to do through my internet searches.
Thank you!
If you to hide the box when you reach the bottom of the page, you javascript should be as follows:
JAVASCRIPT:
$(document).ready(function() {
$(document).on("scroll", function(){
if ( window.scrollMaxY == window.scrollY ) {
$("#box").hide();
}
})
});
HTML:
<div id="box">
<div>Chamber 1</div>
<div>Chamber 2</div>
<div>Chamber 3</div>
</div>
You should make sure that the div has id "box". If you're working with a div of class "box" then you would use:
$(document).ready(function() {
$(".box").hide();
});
I think this might help you and would be better to understand. A good explantion is given below here with demo.
$(window).scroll(function() {
if ($(window).scrollTop() + $(window).outerHeight() == $(document).outerHeight()) {
//Script for activity on reaching bottom of document
$("#box").fadeOut();
} else // optional
{
$("#box").fadeIn();
}
});
body {
margin: 0px;
width: 100%;
}
.container {
position: relative;
height: 900px;
width: 100%;
background: #fee;
}
#box {
position: fixed;
top: 50px;
left: 0px;
background: lightblue;
height: auto;
padding: 15px;
overflow: hidden;
max-width: 250px;
width: 210px;
}
#box > div {
margin: 5px;
background: #F33636;
padding: 10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div class="container">
</div>
<div id="box">
Box
<hr>
<div class="chamber1">
Chamber 1
</div>
<div class="chamber2">
Chamber 2
</div>
</div>
JSFiddle
You can play around with Fiddle Link.
I have div container with list (cards) inside. When I hover it, cards start to moving (translateX animation). container's width is 300px, elements count in container:3, each element width:100px.
So you can see 3 elements in container together overflow:hidden. What I want to make?, is that when there is no element to show translateX animation -100px = 100px blank space after third element, it start from 1 elements in the list immediately after last, with no blank space.
For now, I have no idea how it could be done without duplicates and etc.
Here is what I have at the moment:
Fiddle (Hover cards to see translation animation)
UPD 1:
The code and data (cards count, container size) was taken for example, i'll try to explain better what i want: My goal is to built list of cards and after button was pressed, the list will start moving (like in example with translateX animation) for some time (for example translateX: 12491px, animation-duration: 15s;) and stops. But problem is that amount of crads in the list would be in range of 3-40 cards (each card is 100px width & height). So, when i'll set translateX: 12491px for example, it will be out of range and after the last card in the list would appear blank space. I want first and last card to be tied somehow and after the last card immediately appears first card in the list and etc.. Maybe i am searching for solution in a wrong way, but i guess you understand the main idea.
UPD 2:
I found that cs:go uses animation that i wanted to write on html\css\js. Here is video: youtube.com
html:
<div class="container">
<div class="cards">
<div class="card">
1
</div>
<div class="card">
2
</div>
<div class="card">
3
</div>
</div>
</div>
css:
.container
{
width:300px;
height: 100px;
border: 2px solid black;
overflow: hidden;
}
.card
{
float:left;
height: 100px;
width: 100px;
background-color:blue;
box-sizing: border-box;
border: 2px solid red;
color: white;
font-size: 23px;
}
.cards:hover
{
transform: translateX(-100px);
transition-duration: 3s;
animation-duration: 3s;
animation-fill-mode: forwards;
}
start from 1 elements in the list immediately after last, with no
blank space
This is beyond CSS and you will need Javascript for that. Because, you have tagged the question with Javascript and not jQuery, my answer would be limited to pure Javascript only. Look ma, no JQuery ;)
I have no idea how it could be done without duplicates
Here is a DIY (do it yourself) idea..
The main trick is to show at least one item less than the total you have. If you have 3 cards, show only 2. If you have 4 cards, show only 3. Why, because you need to re-position a card when it goes out of view and wrap it back at the end. If you show exactly the same number of cards that you have, then you cannot break half-a-card and wrap it and you will see some blank space until the first one goes out of view. You get the idea?
Do not use translate or you will end up complicating things for yourself while scripting it out. Keep things simple.
Do not use a wrapper for your cards. Why? Because, we will be re-positioning the cards which have gone out of view. When we do that, the next card will take up its place and immediately go out of view making things further difficult for you.
To keep things simple, arrange your cards with absolute positioning relative to its container. To start with, let all cards stack up at top:0; and left: 0;.
Next wire-up Javascript to position the left property based on the width of each card and arrange them linearly.
Use requestAnimationFrame to control the animation.
Keep track of the left-most card and its left position. When this goes out of view (which is 0 minus width), appendChild this card to its container. This will move the card to the end of cards. Also, change the left property to it based on the last card in the list.
That' all there is to it.
Below is a demo. To make it easy for you to experiment, I have used a settings object to keep the configurable properties which you can easily tweak and see. Look closely at the code and you will find it simple to understand. You can set the iterations settings to 0 to make the animation infinite.
Also, note that you do not need to duplicate or fake the cards. Try the demo and add as many cards you want to.
The inline code comments in the snippet, will further help you understand each line of code and relate to the steps above.
Snippet:
var list = document.querySelector('.cardList'), // cache the container
cards = document.querySelectorAll('.card'), // cache the list of cards
start = document.getElementById('start'), // buttons
stop = document.getElementById('stop'),
reset = document.getElementById('reset'),
raf, init = 0, counter = 0, lastCard, currentIteration = 0, // general purpose variables
settings = { // settings object to help make things configurable
'width': 100, 'height': 100, 'speed': 2,
'iterations': 2, 'count': cards.length
}
;
start.addEventListener('click', startClick); // wire up click event on buttons
stop.addEventListener('click', stopClick);
reset.addEventListener('click', resetClick);
initialize(); // initialize to arrange the cards at start
function initialize() {
// loop thru all cards and set the left property as per width and index position
[].forEach.call(cards, function(elem, idx) {
elem.style.left = (settings.width * idx) + 'px';
});
init = -(settings.width); // initialize the view cutoff
lastCard = cards[settings.count - 1]; // identify the last card
counter = 0; currentIteration = 0; // reset some counters
settings.speed = +(document.getElementById('speed').value);
settings.iterations = +(document.getElementById('iter').value);
}
function startClick() {
initialize(); raf = window.requestAnimationFrame(keyframes); // start animating
}
function stopClick() { window.cancelAnimationFrame(raf); } // stop animating
function resetClick() { // stop animating and re-initialize cards to start again
window.cancelAnimationFrame(raf);
document.getElementById('speed').value = '2';
document.getElementById('iter').value = '2';
initialize();
}
// actual animation function
function keyframes() {
var currentCard, currentLeft = 0, newLeft = 0;
// iterate all cards and decrease the left property based on speed
[].forEach.call(cards, function(elem, idx) {
elem.style.left = (parseInt(elem.style.left) - settings.speed) + 'px';
});
currentCard = cards[counter]; // identify left-most card
currentLeft = parseInt(currentCard.style.left); // get its left position
if (currentLeft <= init) { // check if it has gone out of view
// calculate position of last card
newLeft = parseInt(lastCard.style.left) + settings.width;
list.appendChild(currentCard); // move the card to end of list
currentCard.style.left = newLeft + 'px'; // change left position based on last card
lastCard = currentCard; // set this as the last card for next iteration
counter = (counter + 1) % settings.count; // set the next card index
if ((settings.iterations > 0) && (counter >= (settings.count - 1))) {
currentIteration++; // check settings for repeat iterations
}
}
if (currentIteration >= settings.iterations) { return; } // when to stop
raf = window.requestAnimationFrame(keyframes); // request another animation frame
};
* { box-sizing: border-box; padding: 0; margin: 0; }
.cardList {
position: relative; height: 100px; width: 300px;
margin: 10px; border: 2px solid #33e;
overflow: hidden; white-space: nowrap;
}
.card {
position: absolute; left: 0; top: 0; text-align: center;
height: 100px; width: 100px; line-height: 100px;
background-color: #99e;
font-family: monospace; font-size: 2em; color: #444;
border-left: 1px solid #33e; border-right: 1px solid #33e;
}
div.controls, button { margin: 10px; padding: 8px; font-family: monospace; }
div.controls input { width: 48px; padding: 2px; text-align: center; font-family: monospace; }
<div class="controls">
<label>Speed <input id="speed" type="number" min="1" max="8" value="2" />x</label>
|
<label>Iterations <input id="iter" type="number" min="0" max="8" value="2" /></label>
</div>
<div class="cardList">
<div class="card">1</div>
<div class="card">2</div>
<div class="card">3</div>
<div class="card">4</div>
</div>
<button id="start">Start</button>
<button id="stop">Stop</button>
<button id="reset">Reset</button>
Fiddle: http://jsfiddle.net/abhitalks/1hkw1v0w/
Note: I have left out a few things in the demo. Especially, although width and height of the cards is part of the settings object, but currently it left fixed. You can easily use the settings object to make the dimensions of the cards configurable as well.
Edit:
(as per Op's comment)
If you want a greater control over distance to scroll, duration and timing-functions (easing), then you could implement those yourself using a library. A couple of such good libraries are the Robert Penner's Easing Functions and a jQuery plugin from GSGD. Although you can implement all of that with pure Javascript, it would be easier if you use a library like jQuery.
Catch here is that in order to do so effectively, you must then duplicate the cards. You can do so easily by cloning the entire list a couple of times.
Although you have not tagged this question with jQuery, here is a small demo (using jQuery to get it done quickly) where you can configure the speed and the distance.
Snippet 2:
var $cardList = $('.cardList').first(),
$cards = $('.card'),
$speed = $('input[name=speed]'),
width = 100,
randomize = true,
distance = 20 * width
;
for (var i = 0; i < 50; i++) {
$cards.clone().appendTo($cardList);
}
function spin() {
var newMargin = 0, newDistance = distance,
speed = +($speed.filter(':checked').val());
if (randomize) {
newDistance = Math.floor(Math.random() * $cards.length * 5);
newDistance += $cards.length * 5;
newDistance *= width;
}
newMargin = -(newDistance);
$cards.first().animate({
marginLeft: newMargin
}, speed);
}
$('#spin').click(function() {
$cards.first().css('margin-left', 0);
spin();
return false;
});
* { box-sizing: border-box; padding: 0; margin: 0; }
.cardList {
height: 100px; width: 302px; position: relative;
margin: 10px; border: 1px solid #33e;
overflow: hidden; white-space: nowrap;
}
.card {
display: inline-block; text-align: center;
height: 100px; width: 100px; line-height: 100px;
background-color: #99e;
font-family: monospace; font-size: 2em; color: #444;
border-left: 1px solid #33e; border-right: 1px solid #33e;
}
.cardList::before, .cardList::after {
content: ''; display: block; z-index: 100;
width: 0px; height: 0px; transform: translateX(-50%);
border-left: 8px solid transparent;
border-right: 8px solid transparent;
}
.cardList::before {
position: absolute; top: 0px; left: 50%;
border-top: 12px solid #33e;
}
.cardList::after {
position: absolute; bottom: 0px; left: 50%;
border-bottom: 12px solid #33e;
}
div.controls, button { margin: 10px; padding: 8px; font-family: monospace; }
div.controls input { width: 48px; padding: 2px; text-align: center; font-family: monospace; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="controls">
<label>Speed: </label>
|
<label><input name="speed" type="radio" value='6000' />Slow</label>
<label><input name="speed" type="radio" value='5000' checked />Medium</label>
<label><input name="speed" type="radio" value='3000' />Fast</label>
</div>
<div class="cardList"><!--
--><div class="card">1</div><!--
--><div class="card">2</div><!--
--><div class="card">3</div><!--
--><div class="card">4</div><!--
--></div>
<button id="spin">Spin</button>
Fiddle 2: http://jsfiddle.net/abhitalks/c50upco5/
If you don't want to modify the dom elements you could take advantage of flex-item's order property;
to do this you'd still need a little JS to add this property after animation has ended;
I also changed to animation instead of transition so it automatically resets the transform property at the end of animation.
$('.cards').mouseenter(function() {
setTimeout(function() {
$('.card').first().css("order", "2");
}, 3000);
});
$('.cards').mouseleave(function() {
$('.card').first().css("order", "-1");
});
.container {
width: 300px;
height: 100px;
border: 2px solid black;
overflow: hidden;
}
.card {
float: left;
/* height: 100px;
width: 100px;*/
background-color: blue;
box-sizing: border-box;
border: 2px solid red;
color: white;
font-size: 23px;
flex: 0 0 25%;
}
.cards:hover {
animation: trans 3s;
}
/**/
.cards {
width: 400px;
height: 100%;
display: flex;
transition: transform 3s;
}
#keyframes trans {
0% {
transform: translateX(0)
}
100% {
transform: translateX(-100px)
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<div class="container">
<div class="cards">
<div class="card">1</div>
<div class="card">2</div>
<div class="card">3</div>
</div>
</div>
fiddle
But if you're OK to use JS I suggest you manipulate the order of DOM elements directly,taking the first child element of .cards and appending it to the end of list at the end of each animation;
try this:
var anim;
$('.cards').mouseenter(function(){
anim = setInterval(function(){
$('.cards').append($('.card').first())
},3000)
});
$('.cards').mouseleave(function(){
clearInterval(anim)
});
.container{
width:300px;
height: 100px;
border: 2px solid black;
overflow: hidden;
}
.card{
float:left;
/* height: 100px;
width: 100px;*/
background-color:blue;
box-sizing: border-box;
border: 2px solid red;
color: white;
font-size: 23px;
/**/
flex:0 0 25%;
}
.cards:hover{
animation: trans 3s infinite;
}
/**/
.cards{
width:400px;
height:100%;
display:flex;
}
#keyframes trans {
0% {
transform: translateX(0)
}
100% {
transform: translateX(-100px)
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<div class="container">
<div class="cards">
<div class="card">
1
</div>
<div class="card">
2
</div>
<div class="card">
3
</div>
</div>
</div>
in case you want one card to be present at same time both at the beginning and at the end of card-list you'll need to make a deep-copy / clone of the element;
here's an example;
Update 2:
I wrote a jquery plugin that may act the way you want:
you can add as many cards as you want, right now the "translateX" is random (the script will choose randomly the final card)
link to the demo
Update:
I know, I used duplicates, but now my code works on three cards:
I added three "fake" cards
Each "real" card has it's own animation
the "fake" cards will be overlapped by the real ones once their cycle is finished ("when there is no element to show" as you asked)
check the snippet:
.container {
width: 300px;
height: 100px;
border: 2px solid black;
overflow: hidden;
}
.card {
float: left;
height: 100px;
width: 100px;
background-color: blue;
box-sizing: border-box;
border: 2px solid red;
color: white;
font-size: 23px;
}
.cards {
width: 600px;
}
.container:hover .card1{
animation: 1600ms slide1 infinite linear;
}
.container:hover .card2{
animation: 1600ms slide2 infinite linear;
}
.container:hover .card3{
animation: 1600ms slide3 infinite linear;
}
.fakecard{z-index:-1000;}
.container:hover .fakecard{
animation: 1600ms fakeslide infinite linear;
}
#keyframes slide1 {
0% { transform: translateX(0px); }
33% { transform: translateX(-100px); }
33.1% { transform: translateX(+200px); }
100% { transform: translateX(0px); }
}
#keyframes slide2 {
0% { transform: translateX(0px); }
66% { transform: translateX(-200px); }
66.1% { transform: translateX(100px); }
100% { transform: translateX(0px); }
}
#keyframes slide3 {
0% { transform: translateX(0px); }
99% { transform: translateX(-300px); }
99.1% { transform: translateX(+300px); }
100% { transform: translateX(0px); }
}
#keyframes fakeslide {
0% { transform: translateX(0px); }
99% { transform: translateX(-300px); }
99.1% { transform: translateX(+300px); }
100% { transform: translateX(0px); }
}
<div class="container">
<div class="cards">
<div class="card card1">
1
</div>
<div class="card card2">
2
</div>
<div class="card card3">
3
</div>
<div class="card fakecard">
1 (fake)
</div>
<div class="card fakecard">
2 (fake)
</div>
<div class="card fakecard">
3 (fake)
</div>
</div>
</div>
Previous answer:
Is this what you are trying to achieve?
I don't think you can do it without duplicates...
If not, can you explain better what you are trying to achieve here?
[snipped code removed]
Here is the same effect that you mentioned, with a little tweak on your CSS and a helpful hand from jQuery.
CSS
Change your selector for the translateX animation to apply on each of the .card boxes when their immediate parent is hovered, and not the .cards (which is the immediate parent of the .cards). This is because you'd want the cards to move to the left, and not the window through which they appear while making the movement.
That is,
.cards:hover .card {
transform: translateX(-100px);
transition-duration: 1.5s;
animation-duration: 1.5s;
animation-fill-mode: forwards;
}
jQuery
var $container = $('.container');
var cardWidth = 100;
$container.on('mouseenter', function (e) {
e.preventDefault();
var $card0Clone = $('.card').eq(0).clone(); // clone of the first .card element
$('.cards').append($card0Clone);
updateWidth();
});
$container.on('mouseleave', function (e) {
e.preventDefault();
var $cards = $('.card');
$cards.eq(0).remove(); // remove the last .card element
});
function updateWidth() {
$('.cards').width(($('.card').length) * cardWidth); // no of cards in the queue times the width of each card would result in a container fit enough for all of them
}
Code Explained
As you move in the mouse pointer, a clone of the first card is created, and appended to the end of the cards collection. Further, as you move the mouse out of the hover area, the original .card (which was cloned earlier) will be removed from the head of the queue - hence, producing a cyclic effect.
The real trick though is with the updateWidth function. Every time the mouse enters the .container the width of the .cards' immediate parent (i.e. .cards div) is updated, so that .cards div is wide enough to fit in all the .cards, and therefore, making sure that each of the cards push against each other and stay in one line at the time the translation animation is being done.
Here is a simple technique that manipulates the Dom to create your desired effect
Javascript:
document.querySelector('.cards').addEventListener('mousedown', function(e) {
if (e.clientX < (this.offsetWidth >> 1)) {
this.appendChild(this.removeChild(this.firstElementChild));
} else {
this.insertBefore(this.lastElementChild, this.firstElementChild);
}});
then in you css use the nth-of-type selector to position elements as required.
Here is your fiddle
If you are using mouseover you might need to wait for transitionend event before firing again.
Check out this demo
Here I used JQuery, you can configure your animation using two variables
var translateX = 1000; //adjust the whole distance to translate
var stepSpeed = 100; //adjust the speed of each step transition in milliseconds
After setting your variables, on the click event of the cards do the following:-
Get the number of the steps required based on translateX
Loop for the number of steps
Inside each loop (each step) move the cards 1 step to the left, then put the first card to the end of the cards to form the connected loop, then return back the cards to it's initial position
Here is the code:
var stepsNumber = translateX/100;
for(var i=0; i< stepsNumber; i++)
{
$('.cards').animate({'left' : -100}, stepSpeed,function(){
$('.cards div:last').after($('.cards div:first'));
$('.cards').css({'left' : '0px'});
});
}
I'm using Isotope on a project, but have run into an issue when using the filter method.
Filtering the list hides the undesired elements fine, but the transitioning animations aren't as smooth as the demos have me believe they should be.
The following code should illustrate my point; the filters apply and the elements do animate, but there are noticeable, visual jumps:
var config = {
itemSelector: '.grid_item',
percentPosition: true,
layoutMode: 'fitRows',
fitRows: {
gutter: '.grid_gutter'
}
}
$('.grid').isotope(config);
$('.filter').on('click', function(){
var type = $(this).attr('id');
$('.grid').isotope({
filter : '.' + type
});
});
$('#clear').on('click', function(){
$('.grid').isotope({
filter : '*'
});
});
html, body {
margin: 0;
padding: 0;
}
.grid {
list-style: none;
margin: 1em;
padding: 0;
text-indent: 0;
}
.grid_item {
background: gray;
margin: 0 0 1%;
padding-top: 40%;
position: relative;
width: 49.5%;
}
.grid_text {
color: #fff;
position: absolute;
top: 50%; left: 0; right: 0; bottom: 0;
text-align: center;
}
.grid_gutter {
width: 1%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.isotope/2.2.1/isotope.pkgd.js"></script>
<button class="filter" id="exit">Exit</button>
<button class="filter" id="creation">Creation</button>
<button id="clear">Clear</button>
<ul class="grid">
<div class="grid_gutter"></div>
<li class="grid_item exit">
<span class="grid_text">Exit</span>
</li>
<li class="grid_item creation">
<span class="grid_text">Creation</span>
</li>
<li class="grid_item creation">
<span class="grid_text">Creation</span>
</li>
</ul>
I assume that it's something in my CSS that's causing the issue, but I can't weed it out.
Also, see this fiddle.
For me the issue was that I had a transition applied to the elements which was causing it to be jerky. Setting the transition to 0s fixed it for me.
I don't know if this is a problem with Isotope or if we were both doing the same wrong thing, but I had the same issue and resolved it by not using percentPosition at all. The Isotope documentation says using percentPosition "reduce[s] adjustment transitions on window resize", so I guess not using it may cause performance problems.
I have four div container in the following order:
Every container has CSS class "chart multi-mode" with style:
.chart.multi-mode {
width: 300px;
height: 200px;
margin: 0 15px 15px 0;
}
Every second div should have no right margin, so I use:
.chart.multi-mode:nth-child(2n) {
margin-right: 0px;
}
This is working fine until I use jQuery sortable()
When dragging one div and holding, jQuery adds an placeholder, which I also gave the classes "chart multi-mode" and the original object becomes absolute and I remove the class "multi-mode".
But nth-Child doesn't care about removing the class "multi-mode" and still counts the absolute div and so the margins are set wrong, see here:
Any solution?
To select the second element with the class, after the changes, there's not much you could do. The only kind of selector I can think of is to set the hierarchy starting from the element that does not contain the class, and follow using immediate forward sibling selector +, to target the second one:
Untested
li:not(.chart.multi-mode) + .chart.multi-mode + .chart.multi-mode {
}
I solved it with Javascript:
this.$chartList.sortable({
placeholder : "chart chart-placeholder " + chartTypeClass,
handle : ".opt-drag",
start : function(event, ui) {
ui.item.addClass('drag-state');
$('.multi-mode:not(.drag-state):odd').addClass('odd');
$('.multi-mode:not(.drag-state):even').addClass('even');
},
change : function(event, ui ) {
$('.multi-mode').removeClass('odd');
$('.multi-mode').removeClass('even');
$('.multi-mode:not(.drag-state):odd').addClass('odd');
$('.multi-mode:not(.drag-state):even').addClass('even');
},
stop : function(event, ui) {
ui.item.removeClass('drag-state');
$('.multi-mode').removeClass('odd');
$('.multi-mode').removeClass('even');
},
update : function() {
uiBehavior.startSpinner();
var newUuidOrder = [];
$('.chart').each(function() {
newUuidOrder.push($(this).attr('uuid'));
});
_this.restSetChartsOrder(newUuidOrder, function() {
uiBehavior.stopSpinner();
});
}
});
On start Draggin or when list change I count elements and add class odd or even.
CSS looks like this:
.chart.multi-mode.odd {
margin-right: 0px !important;
}
.chart.multi-mode.even {
margin-right: 15px !important;
}
Thanks to all!
I knocked something workable up that uses a different approach laying out the boxes (see my comment Weird behavior of CSS nth-child after editing dom):
Basically is a non-responsive light version of a Bootstrap grid:
* { box-sizing: border-box; }
body {
padding: 50px;
}
#chartlist {
list-style: none;
margin: 0 -8px;
padding: 0;
width: 600px;
}
#chartlist::after {
content:"";
display:table;
clear:both;
}
.chart.multi-mode {
float: left;
width: 300px;
height: 200px;
margin-bottom: 16px;
padding: 0 8px;
position: relative;
}
.chart-holder {
border: 1px solid #333;
background: #999;
height: 100%;
}
.chart.placeholder {
background: rgba(255, 0, 0, 0.5);
position: absolute;
right: -20px;
top: 20px;
}
<ul id="chartlist">
<li class="chart multi-mode">
<div class="chart-holder">Chart #1</div>
</li>
<li class="chart multi-mode">
<div class="chart-holder">Chart #2</div>
</li>
<li class="chart multi-mode placeholder">Chart Placeholder</li>
<li class="chart multi-mode">
<div class="chart-holder">Chart #3</div>
</li>
<li class="chart multi-mode">
<div class="chart-holder">Chart #4</div>
</li>
</ul>
(Or http://jsfiddle.net/maryisdead/vfm5d3au/)
negate the placeholder using this
.chart.multi-mode:not(.chart-placeholder):nth-child(2n)