CSS drop down menu positioning in IE7 [closed] - css-position

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
I have a CSS only drop down menu that works fine in firefox and chrome. The sub menus line up directly underneath their parents. However, in IE7, they are positioned off to the side.
CSS:
/* MENU LEVEL 1 */
#nav-img { padding-top: 4px; }
#main-nav {
height: 30px; /* set to the height you want your menu to be */
margin: 0 0 10px; /* just to give some spacing */
width: 600px;
display: inline-block;
font-family: Tahoma, Geneva, sans-serif;
font-size: 10pt;
text-transform: uppercase;
}
#main-nav ul {
margin: 0; padding: 0; /* only needed if you have not done a CSS reset */
}
#main-nav li {
display: block;
border: 1px solid #000;
float: left;
line-height: 30px; /* this should be the same as your #main-nav height */
height: 30px; /* this should be the same as your #main-nav height */
margin: 0; padding: 0; /* only needed if you don't have a reset */
position: relative; /* this is needed in order to position sub menus */
}
#main-nav li a {
display: block;
height: 30px;
line-height: 30px;
padding: 0 15px;
}
#main-nav .current-menu-item a, #main-nav .current_page_item a, #main-nav a:hover {
color: #000;
}
/* MENU LEVEL 2 */
.sub-menu {
text-transform: none;
}
#main-nav ul ul { /* this targets all sub menus */
display: none; /* hide all sub menus from view */
position: absolute;
top: 30px; /* this should be the same height as the top level menu -- height + padding + borders */
}
#main-nav ul ul li { /* this targets all submenu items */
float: none; /* overwriting our float up above */
width: 240px; /* set to the width you want your sub menus to be. This needs to match the value we set below */
}
#main-nav ul ul li a { /* target all sub menu item links */
padding: 5px 10px; /* give our sub menu links a nice button feel */
}
#main-nav ul li:hover > ul {
display: block; /* show sub menus when hovering over a parent */
}
HTML:
<div id="main-nav">
<ul>
<li>Home</li>
<li>Services <br />
<ul class="sub-menu">
<li>Commodities</li>
<li>Professional Practice</li>
<li>Shipping</li>
<li>Commercial</li>
<li>Corporate Structure</li>
<li>Dispute Resolution and Arbitration</li>
<li>International Employment</li>
<li>Financial Re-structuring</li>
<li>Intellectual Property</li>
</ul>
</li>
<li>Team</li>
<li>News</li>
<li>Testimonials</li>
<li>Contact</li>
</ul>
Any help would be much appreciated.

Try this;
#main-nav ul ul {
display: none; /* hide all sub menus from view */
position: absolute;
top: 30px; /* this should be the same height as the top level menu -- height + padding borders */
left: 0;
}
IE7 needs a left value.

Related

which files are necessary for bootstrap dropdown to work?

I am avoiding including the entire bootstrap source code in my app. All I need at this point is the bootstrap dropdown classes. I am trying to make a simple dropdown that looks like this...
<div class="dropdown">
<a data-toggle="dropdown" class="dropdown-toggle">display dropdown</a>
<ul
class="dropdown-menu dropdown-menu-right dropdown-menu-arrow dropdown-scrollable dropdown-content"
>
<li class="dropdown-item">Option 1</li>
<li class="dropdown-item">Option 2</li>
</ul>
</div>
I copied dropdown.scss from the tabler.io library.
This is all it contains...
.dropdown {
display: inline-block;
color: orange;
}
.dropdown-menu {
box-shadow: $dropdown-box-shadow;
min-width: 12rem;
}
.dropdown-item {
color: $text-muted-dark;
z-index: 1000;
}
.dropdown-menu-arrow {
&:before {
position: absolute;
top: -6px;
left: 12px;
display: inline-block;
border-right: 5px solid transparent;
border-bottom: 5px solid $border-color;
border-left: 5px solid transparent;
border-bottom-color: rgba(0, 0, 0, 0.2);
content: "";
}
&:after {
position: absolute;
top: -5px;
left: 12px;
display: inline-block;
border-right: 5px solid transparent;
border-bottom: 5px solid #fff;
border-left: 5px solid transparent;
content: "";
}
&.dropdown-menu-right {
&:before,
&:after {
left: auto;
right: 12px;
}
}
}
.dropdown-toggle {
user-select: none;
cursor: pointer;
&:after {
vertical-align: 0.155em;
}
&:empty:after {
margin-left: 0;
}
}
.dropdown-icon {
color: $text-muted;
margin-right: 0.5rem;
margin-left: -0.5rem;
width: 1em;
display: inline-block;
text-align: center;
vertical-align: -1px;
}
I know that my code is referencing this stylesheet because the font color is orange and the stylesheet's first rule includes color: orange.
I also know that my code detects the user clicking on the text because I tested by adding #click.prevent="doSomething()" where doSomething() simply console logged a message.The message did indeed print out in the Chrome dev tools console.
However, when I click on the words display dropdown, the dropdown menu does not open.
I understand there are javascript files I may need. Which files are those and how can I make sure my code uses this file? This is a Vuejs app using Nuxt.

I would like to delay something after it's been clicked

I'm creating a multiple choice question program and I have it so that once the correct answer is chosen a new question is generated.
Although I want it to go to the next question automatically, I want it to delay for about 0.5 seconds so the user can see that their answer is correct.
I chose to remove the class and replace it with another class so that the background changes colour. Once the new question comes up I want all the colours to return to normal, so I once again remove the new class and replace it with the old class.
If I don't advance to the new question automatically, the colours come up just the way I like them, but if I create it so that it moves on automatically, I am unable to keep the display the same.
After searching through the forums I read that setTimeout should work, but I haven't had much success. I have also tried doing animations so that it takes time, but that didn't work for me either. The animations worked fine, but it still went on to the new set of questions.
I'll include the whole program as it might be better but the section I'm working on is under the function check().
I've been trying to figure out how to delay something for a long time but have been completely unsuccessful. Oh, please be kind to me, I have very little experience. I have only learned how to do javascript by doing the khan academy course. Thanks!
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Project: listening to sounds </title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<!-- ***************** CSS styles ***************** -->
<style>
body {
font-family: comic sans ms, sans-serif;
background-image: url("background.jpg");
background-color: rgb(216, 252, 252);
}
form {
font-size: 1.2em;
}
#text {
background-color: wheat;
width: 150px;
height: 25px;
color: blue;
font-size: 1em;
}
.SoundBite {
float: left;
clear: none;
position: absolute;
top: 130px;
left: 100px;
padding: 5px;
background: darkblue;
color: white;
height: 120px;
width: 200px;
border-radius: 50px;
margin: auto;
text-align: center;
vertical-align: middle;
font-size: 5em;
}
.SoundBite:hover {
background-color: darkgreen;
cursor: pointer;
}
.Score {
float: right;
clear: none;
position: absolute;
top: 50px;
left: 140px;
padding: 5px;
background: darkblue;
border-color: pink;
border: 5px;
opacity: 0.8;
color: white;
height: 45px;
width: 120px;
border-radius: 50px;
margin: auto;
text-align: center;
vertical-align: middle;
font-size: 2em;
pointer-events: none;
}
.Answer {
position: absolute;
color: white;
height: 45px;
width: 120px;
padding: 5px;
border-radius: 25px;
margin: auto;
text-align: center;
vertical-align: middle;
font-size: 2em;
background-color: darkblue;
}
.AnswerCorrect {
position: absolute;
color: white;
height: 45px;
width: 120px;
padding: 5px;
border-radius: 25px;
margin: auto;
text-align: center;
vertical-align: middle;
font-size: 2em;
background-color: green;
}
.Answer:hover {
background-color: #e44404;
cursor: pointer;
}
.AnswerWrong {
position: absolute;
color: white;
height: 45px;
width: 120px;
padding: 5px;
border-radius: 25px;
margin: auto;
text-align: center;
vertical-align: middle;
font-size: 2em;
background-color: red;
}
#Answer1 {
top: 20px;
left: 400px;
}
#Answer2 {
top: 85px;
left: 400px;
}
#Answer3 {
top: 150px;
left: 400px;
}
#Answer4 {
top: 220px;
left: 400px;
}
#Answer5 {
top: 290px;
left: 400px;
}
</style>
</head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<body>
<audio id="audio" src="audio/rug.mp3" autostart="false" ></audio>
<audio id = "win" src="audio/win.mp3" autostart = "true"></audio>
<audio id = "lose" src = "audio/lose.mp3" autstart = "true"></audio>
<a onclick="playSound();"><div span class="SoundBite"><i class="fa fa-file-sound-o" id="audio" src="audio/rug.mp3" autostart="false" style="font-size:64px;color:skyblue"></i></div></a>
<a onclick ="checkAnswer = 1; check();"><div class = "Answer" id="Answer1">1</div>
<a onclick ="checkAnswer = 2; check();"><div class = "Answer" id="Answer2">2</div>
<a onclick ="checkAnswer = 3; check();"><div class = "Answer" id="Answer3">3</div>
<a onclick ="checkAnswer = 4; check();"><div class = "Answer" id="Answer4">4</div>
<a onclick ="checkAnswer = 5; check();"><div class = "Answer" id="Answer5">5</div>
<div class = "Score">Score</div>
<!-- <a onclick ="next();"><div id = "Next">Start</div> -->
<!-- ********************************* Javascript programming follows ********************************* -->
<script>
$(function() {
});
// declare the variables to be used ... do I need global variables? Maybe I should think about these...
var word, ext, directory, wordPosition, decoyWordPosition, answerPosition, decoyAnswerPosition, answer, checkAnswer, correct, incorrect, tries;
directory = "audio/";
ext = ".mp3";
correct = 0;
incorrect = 0;
tries = 0;
// list of words that are spoken
word = ["dam", "dog", "dug", "cat", "cot", "cut", "ran", "rot", "rug"];
wordPosition = Math.floor(Math.random()*word.length); // returns a random array wordPosition
/***************************************************************************
** Functions:
**
**************************************************************************/
// function to display word
function displayWord() {
//$(".SoundBite").text(word[wordPosition]);
//$("#audio").attr("src", directory+word[wordPosition]+ext);
}
// function to display the answer in one of the positions that are assigned
function displayAnswer(answerNumber, wordNumber) {
$("#Answer"+answerNumber).text(word[wordNumber]);
$("#audio").attr("src", directory+word[wordPosition]+ext);
}
// function to play the sound
function playSound() {
var sound = document.getElementById("audio");
sound.play();
}
// function to play a winning sound
function win() {
var sound = document.getElementById("win");
sound.play();
}
// function to play a losing sound
function lose() {
var sound = document.getElementById("lose");
sound.play();
}
function timer() {
new Date().toLocaleTimeString();
}
function check() {
if (answer == checkAnswer) {
//$("#Answer"+checkAnswer).animate({height: "45px", opacity: '0'})
//.animate({height: "45px", opacity: '1.0'});
$("#Answer"+checkAnswer).animate({height: "45px", opacity: '0'})
.animate({height: "45px", opacity: '1.0'})
.removeClass("Answer").addClass("AnswerCorrect");
win();
correct++;
tries++;
refresh();
next();
// need a delay function -- can't get it to work.
} else {
$("#Answer"+checkAnswer).removeClass("Answer").addClass("AnswerWrong"); //css({'background-color': 'red'});
lose();
incorrect++;
tries++;
}
// write in the score
$(".Score").text(Math.round(correct/tries*100)+"%");
}
// making a function to populate the answers
function populateAnswers() {
for (var i = 0; i < 6; i++) {
if (answerPosition < 6) {
if (decoyWordPosition == wordPosition) {
answer=answerPosition
}
displayAnswer(answerPosition, decoyWordPosition);
answerPosition++;
decoyWordPosition++;
if (decoyWordPosition >= word.length) { // want to make sure that the words are within the array
decoyWordPosition = 1 // reset to the beginning to 'wrap' the array.
}
} else {
answerPosition = 1;
}
}
}
// using a random generator to place the answer in a random spot 1 through 4
function randomGenerator() {
answerPosition = Math.floor(Math.random()*4)+1;
}
// returns a random array wordPosition
function randomWord() {
wordPosition = Math.floor(Math.random()*word.length);
}
function refresh() {
for (var i = 1; i <= 5; i++) {
$("#Answer"+i).removeClass("AnswerWrong").addClass("Answer");
$("#Answer"+i).removeClass("AnswerCorrect").addClass("Answer");
}
}
/***************************************************************************
* program as a function *
**************************************************************************/
function next() {
$("#Next").text("Continue");
randomWord();
randomGenerator();
// make the decoy answers randomly
if (wordPosition == 0 || wordPosition == 1) {
// In case the array is at the beginning: make the decoy start at the same spot as the wordPosition
decoyWordPosition = wordPosition;
} else {
// start the decoy word after the word
decoyWordPosition = wordPosition -2;
}
populateAnswers();
}
next();
</script>
</body>
</html>
Just for other people who are trying to find the solution to the setTimeout feature.
I was using it like this:
setTimeout(myFunction(), 2000);
However, it won't work with the brackets after the function. You need to omit those brackets:
setTimeout(myFunction, 2000);

How to create range slider in angular 5

I tried ng2-ion-range-slider and ng2-nouislider using documentation described in github.
When i add these component it showing "it is not a known element of ngModule"
I import ng2-nouislider, ng2-ion-range-slider in my app module
APP MODULE
import 'hammerjs';
import {SharedModule} from './core/modules/shared.module';
import {AppComponent} from './app.component';
import {VoifinityMainModule} from './main/main.module';
import {FuseSplashScreenService} from './core/services/splash-screen.service';
import {VoifinityConfigService} from './core/services/config.service';
import {FuseNavigationService} from './core/components/navigation/navigation.service';
import {AppRoutingModule} from './app-routing.module';
import {AuthenticationModule} from './authentication/authentication.module';
import {VoifinityAuthInterceptor} from './core/auth/voifinity-auth.interceptor';
import {AuthService} from './core/auth/auth.service';
import {AuthGuardService} from './core/auth/auth-guard.service';
import {SimpleNotificationsModule} from 'angular2-notifications';
import {AppService} from './app.service';
import {NgHttpLoaderModule} from 'ng-http-loader/ng-http-loader.module';
import {IonRangeSliderModule} from 'ng2-ion-range-slider';
#NgModule({
declarations: [
AppComponent
],
imports : [
BrowserModule,
HttpClientModule,
BrowserAnimationsModule,
AppRoutingModule,
SharedModule,
SimpleNotificationsModule.forRoot(),
AuthenticationModule,
VoifinityMainModule,
NgHttpLoaderModule,
IonRangeSliderModule
],
providers : [
AuthService,
AppService,
AuthGuardService,
{
provide: HTTP_INTERCEPTORS,
useClass: VoifinityAuthInterceptor,
multi: true
},
FuseSplashScreenService,
VoifinityConfigService,
FuseNavigationService
],
bootstrap : [
AppComponent
]
})
export class AppModule
{
}
app.component.html, added ion-range slider component. but it shows template parse error. ion-range-slider is not a known element
<ion-range-slider #sliderElement
type="double"
[min]="myMinVar"
max="100"
from="28"
from_min="10"
from_max="30"
from_shadow="true"
to="40"
to_min="40"
to_max="90"
to_shadow="true"
grid="true"
grid_num="10"
prefix="Weight: "
postfix=" million pounds"
decorate_both="false"
(onUpdate)="myOnUpdate($event)"
(onChange)="myOnChange($event)"
(onFinish)="myOnFinish($event)"></ion-range-slider>
Seems to be working fine in my case after installing the required dependencies, have a look at my working demo.
Don't forget to include NouisliderModule in the imports of your app.module, and #import "~nouislider/distribute/nouislider.min.css"; in your styles.css
giphy animated gif link
I have developed a range slider but it uses html css and angular (typescript). You may have to modify code slightly but it works by using a CSS circle, placing the number value at a position in that circle, and placing that over the input range.
html section
<div class="row small-margin"><div class="col-lg-8 small-padding">
<div class="form-group has-success">
<label>Credit Score</label>
<br />
<div class="ui medium-padding">
<div class="slidecontainer">
<input #creditSlider (input)="updateCreditSlider()" (change)="updateCreditSlider()" type="range" min="300" max="850" value="{{ creditSliderValue }}" class="slider" id="myRange">
</div>
</div>
<div #creditSliderSpan class="sliderValue circle" >
<span class="noselect">{{ creditSliderValue }}</span>
</div>
<div #creditSliderTrack class="sliderTrack" ></div>
</div>
typescript section (In the angular component class)
creditSliderValue : any;
#ViewChild('creditSlider') creditSlider;
#ViewChild('creditSliderSpan') creditSliderSpan;
.
.
.
updateCreditSlider() {
let horizontalOffset:number = 0;
//values from 300 to 850 - Next value needs to be adjusted based on your placement of slider object
horizontalOffset = ( (Number.parseInt(this.creditSlider.nativeElement.value )- 280)/2.45);
this.creditSliderSpan.nativeElement.style.left = ( horizontalOffset )+ 'px';
this.creditSliderSpan.nativeElement.style.top = this.creditSliderSpan.nativeElement.style.top + 'px';
this.creditSliderValue = this.creditSlider.nativeElement.value;
}
css section
.smallPadding {
margin-bottom: 0px;
padding: 4px;
}
.slidecontainer {
width: 100%; /* Width of the outside container */
}
/* The slider itself */
.slider {
position: relative;
-webkit-appearance: none; /* Override default CSS styles */
appearance: none;
width:275px;
//width: 100%; /* Full-width */
height: 15px;
border-radius: 5px;
//background: #d3d3d3; /* Grey background */
background: rgba(211,211,211, 0.00);
outline: none; /* Remove outline */
opacity: 0.7; /* Set transparency (for mouse-over effects on hover) */
-webkit-transition: .2s; /* 0.2 seconds transition on hover */
transition: opacity .2s;
z-index: 20;
}
/* Mouse-over effects */
.slider:hover {
//opacity: 1; /* Fully shown on mouse-over */
}
/* The slider handle (use -webkit- (Chrome, Opera, Safari, Edge) and -moz- (Firefox) to override default look) */
.slider::-webkit-slider-thumb {
position: relative;
-webkit-appearance: none; /* Override default look */
appearance: none;
width: 50px; /* Set a specific slider handle width */
height: 50px;
border-radius: 50%;
border-style: none;
//background: #4CAF50; /* Green background */
background: rgba(76,175,80, 0.00);
cursor: pointer; /* Cursor on hover */
}
.slider::-moz-range-thumb {
width: 25px; /* Set a specific slider handle width */
height: 25px;
border-radius: 50%;
border-style: none;
//background: #4CAF50; /* Green background */
background: rgba(76,175,80, 0.00);
cursor: pointer; /* Cursor on hover */
}
.sliderValue {
position: absolute;
top: 25px;
left: 0px;
width: 100%; /* Width of the outside container */
z-index: 15;
}
.noselect {
-webkit-touch-callout: none; /* iOS Safari */
-webkit-user-select: none; /* Safari */
-khtml-user-select: none; /* Konqueror HTML */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none; /* Non-prefixed version, currently
supported by Chrome and Opera */
}
#media screen and (max-width: 992px) {
}
.circle {
width: 50px;
height: 50px;
border-radius: 50%;
font-size: 14pt;
color: black;
line-height: 46px;
text-align: center;
background: white;
vertical-align: center;
//display: table-cell;
border-style: solid;
border-color: #7E7E7E;
}
.medium-padding {
padding-top: 14px;
padding-bottom: 14px;
}
.sliderTrack {
position: relative;
width: 220px;
height: 10px;
background-color: #E0E1E2;
vertical-align: center;
border-radius: 5px;
top: -35px;
left: 25px;
//display: table-cell;
//border-style: solid;
//border-color: #7E7E7E;
}
.left-padding {
padding-left: 14px;
}
.small-margin {
margin-left: 4px;
}
.medium-margin {
margin-left: 10px;
}
.small-padding{
padding: 4px;
}

Media query fails for only one property

I am unable to change padding for one element on mobile devices. The queries are working for several properties, but padding will not work (neither will line height if I try to use that). Basic styling in custom css is:
#topright {
font-size: 20px;
letter-spacing: 4px;
color: rgba(255,255,255,1.0);
padding-top: 8px !important;
padding-bottom: 8px !important;
font-weight: 200;
}
Media query for phone is
/* Custom, iPhone Retina */
#media only screen and (min-width : 320px) {
.header-2 .logo {
width: 250px;
}
.footer-widget ul li {
width: 100%;
}
.footer-widget ul {
padding-left: 0;
padding-right: 0;
}
div.vc_column-inner vc_custom_1476556729591 {
padding-left: 0;
padding-right: 0;
}
.footer-widget .textwidget p {
text-align: center;
}
#topright {
padding-top: 1px;
padding-bottom: 1px
}
}
The smaller padding number will not be applied. If I remove !important from main css, then the phone query gets applied to all devices. It's weird because all the other properties for the phone query are working fine.
From this helpful page on media queries, min-width: 320px means:
"If [device width] is greater than or equal to 320px, then do {...}"
In other words, the media query you think you created to target only iPhone will actually be firing for all devices which have a width of 320px or greater. Instead, I think you intended to use max-width
So use this CSS:
/* Custom, iPhone Retina */
#media only screen and (max-width : 320px) {
.header-2 .logo {
width: 250px;
}
...
#topright {
padding-top: 1px;
padding-bottom: 1px
}
}
And you should also remove the !important directives from your main.css file.

CSS TranslationX of container not working in Safari

Today I have noticed a weird behavior of Safari (9.0) when I applied a transition to an element that was translating on the X axis while the width was also increasing.
I have reproduced the behavior in this JsFiddle. Here is an embed code for those who like it better. In Firefox and Chrome it looks pretty smooth but not in Safari, does anyone have a solution or a best way to achieve the same effect?
var button = document.getElementsByTagName('button')[0],
container = document.getElementsByTagName('div')[0];
button.addEventListener('click', function() { container.classList.toggle('open'); });
.container {
width: 100%;
overflow: hidden;
}
ul {
display: block;
width: 100%;
padding: 0;
margin: 0;
transition: width 1s, transform 1s;
}
.open ul {
width: 200%;
transform: translateX(-50%);
}
li {
/* Just some style first */
font-family: sans-serif;
color: #fff;
text-align: center;
text-transform: uppercase;
background-color: red;
padding: 1em 0;
display: inline-block;
width: calc(50% - 4px);
}
li:first-child {
background-color: green;
}
<div class="container">
<ul>
<li>Test</li>
<li>Test</li>
</ul>
</div>
<button type="button">Toggle translation</button>
Re-posting as an answer.
Here is the jsFiddle result and snippet as below:
var button = document.getElementsByTagName('button')[0];
var container = document.getElementsByTagName('div')[0];
var timeline = new TimelineMax({ paused: true });
timeline.to('ul', 1, { width: '200%', xPercent: -50, ease: Power2.easeInOut });
button.addEventListener('click', function() {
timeline.progress() > 0 ? timeline.reverse() : timeline.play();
});
.container {
width: 100%;
overflow: hidden;
}
ul {
display: block;
width: 100%;
padding: 0;
margin: 0;
}
li {
font-family: sans-serif;
color: #fff;
text-align: center;
text-transform: uppercase;
background-color: red;
display: inline-block;
padding: 1em 0;
width: calc(50% - 4px);
}
li:first-child {
background-color: green;
}
<script src="//cdnjs.cloudflare.com/ajax/libs/gsap/1.18.2/TweenMax.min.js"></script>
<div class="container">
<ul>
<li>Test</li>
<li>Test</li>
</ul>
</div>
<button type="button">Toggle translation</button>
Hope this is helpful.
P.S. I have been using GSAP for quite a while now and I don't remember getting stuck on any browser-specific issues unless a browser would do something differently. A little research into GSAP and it would tell you that browser compatibility is one of their main selling points.
By animating margin-left instead of translateX the result is acceptable in Safari:
var button = document.getElementsByTagName('button')[0],
container = document.getElementsByTagName('div')[0];
button.addEventListener('click', function() {
container.classList.toggle('open');
});
.container {
width: 100%;
overflow: hidden;
}
ul {
display: block;
width: 100%;
padding: 0;
margin: 0;
transition: width 1s, margin-left 1s;
}
.open ul {
width: 200%;
margin-left:-100%;
}
li {
font-family: sans-serif;
color: #fff;
text-align: center;
text-transform: uppercase;
background-color: red;
display: inline-block;
padding: 1em 0;
width: calc(50% - 4px);
}
li:first-child {
background-color: green;
}
<div class="container">
<ul>
<li>Test</li>
<li>Test</li>
</ul>
</div>
<button type="button">Toggle translation</button>
Using scaleX instead of animating width is smoother, but probably not what you want.
var button = document.getElementsByTagName('button')[0],
container = document.getElementsByTagName('div')[0];
button.addEventListener('click', function() {
container.classList.toggle('open');
});
.container {
width: 100%;
overflow: hidden;
}
ul {
display: block;
width: 100%;
padding: 0;
margin: 0;
transition: transform 1s;
}
.open ul {
transform: translateX(-50%) scaleX(2);
}
li {
font-family: sans-serif;
color: #fff;
text-align: center;
text-transform: uppercase;
background-color: red;
display: inline-block;
padding: 1em 0;
width: calc(50% - 4px);
}
li:first-child {
background-color: green;
}
<div class="container">
<ul>
<li>Test</li>
<li>Test</li>
</ul>
</div>
<button type="button">Toggle translation</button>
So, I will try to sum up the two best solutions here : one with CSS transform and the other with Javascript animation (GSAP).
CSS TRANSFORM
In terms of performance, it is recommended to only animate transforms (translate, scale, rotate) and opacity. If you are interested in more optimisation details you can have a look at this article by Anna Migas.
So, as #Meiko suggested, the best solution is to only animate scale and translate properties. Here is a code sample (and the JSFiddle)
var button = document.getElementsByTagName('button')[0],
container = document.getElementsByTagName('div')[0];
button.addEventListener('click', function() {
container.classList.toggle('open');
})
.container,
ul {
width: 100%;
}
ul {
overflow: hidden;
/* reset default browser styles */
padding: 0;
margin: 0;
}
li {
display: inline-block;
width: calc(50% - 2px);
position: relative;
transition: transform 1s;
/* Just some style */
font-family: sans-serif;
color: #fff;
text-align: center;
text-transform: uppercase;
padding: 1em 0;
}
li::before {
content: '';
position: absolute;
top: 0;
left: 0;
display: block;
width: 100%;
height: 100%;
background-color: red;
z-index: -1;
transition: transform 1s;
}
li:first-child::before {
background-color: green;
}
.open li:first-child {
transform: translateX(-100%);
}
.open li:nth-of-type(2) {
transform: translateX(-50%);
}
.open li:nth-of-type(2)::before {
transform: scaleX(2);
}
<div class="container">
<ul>
<li>Test</li>
<li>Test</li>
</ul>
</div>
<button type="button">Toggle translation</button>
PROS:
Only use a tiny bit of Javascript to toggle class,
The browser support is quite good (needs vendor-specific properties and some testing),
Really fast and light on GPU memory.
CONS:
Pretty limited in terms of usage (the actual width of the second cell stays the same),
Needs more lines of CSS.
JS ANIMATION (WITH GSAP)
This solution has been suggested by #Tahir Ahmed and use the GSAP library. As a side note, I really think that this is the best js library out there for this kind of animation. Here is a snippet of how it works (and the JSFiddle):
var button = document.getElementsByTagName('button')[0],
timeline = new TimelineMax({ paused: true });
timeline.to('ul', 1, { width: '200%', xPercent: -50 });
button.addEventListener('click', function() {
timeline.progress() > 0 ? timeline.reverse() : timeline.play();
})
.container {
width: 100%;
overflow: hidden;
}
ul {
width: 100%;
/* reset default browser styles */
padding: 0;
margin: 0;
}
li {
display: inline-block;
width: calc(50% - 2px);
background-color: red;
/* Just some style */
font-family: sans-serif;
color: #fff;
text-align: center;
text-transform: uppercase;
padding: 1em 0;
}
li:first-child {
background-color: green;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.18.2/TweenMax.min.js"></script>
<div class="container">
<ul>
<li>Test</li>
<li>Test</li>
</ul>
</div>
<button type="button">Toggle translation</button>
PROS:
Really flexible, sky is the limit!
You can animate properties such as display (you can't in CSS),
Compatible with every browser out there (down to IE6).
CONS:
Require a third party library (about 30kb),
Seems a bit harder for the GPU (although it needs more testing to be sure).
In the end it really depends on the animation you need but if it get's a little bit more complex than moving a container around then I will choose GSAP.