I've done an example to demonstrate what I expect:
I have a toggle. The toggle's width may vary depending on its parent container.
And I want to use transform
to control the position of the toggle-thumb, because this would result in an animation effect.
In this demo, everything works fine when the parent container has a width of 50px
.
However, if you click the "ToggleWidth" button to make the toggle's parent container into a 100px width element. The transform
looks bad.
I tried something like this:
transform:translate(calc(100% - x), -50%);
But the position of the thumb is still not working properly.
Is there any way I can fix it with pure CSS?
$('#dark-switch').click(()=>{
$('#dark-toggle-thumb').toggleClass('isOn');
});
$('#toggleWidth').click(()=>{
$('#dark-switch').toggleClass('dark-switch-vary');
})
body {padding:2em}
.dark-switch-vary {
max-width:100px !important;
}
#dark-switch {
position:relative;
width:90%;
max-width:50px;
}
#dark-toggle-back {
width: calc(100% - 9px);
background: #666;
height: 24px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
border-radius: 99px;
}
#dark-toggle-thumb {
background: #ccc;
height: 17px;
border-radius: 99px;
width: 17px;
top: 50%;
transform: translate(6px, -50%);
position: absolute;
transition: .2s;
}
#dark-toggle-thumb svg {
width: 15px;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
.isOn {
transform: translate(26px, -50%) !important;
/* How do I make the translateX value flexible rather than a fixed number? */
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="dark-switch">
<section id="dark-toggle-back"></section>
<section id="dark-toggle-thumb">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 3a7 7 0 0 0 7 9h2a9 9 0 1 1-9-9Z"></path></svg>
</section>
</div>
<br><br><br><br><br>
<button id="toggleWidth">ToggleWidth</button>
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
CodePudding user response:
Because of the way CSS works, you can't change predefined numbers. I would suggest using pure jQuery to help with doing switches (aka using $('#dark-toggle-thumb').css("translate: " number of pixels you want "px, -50%;");
. Since you wanted CSS though, you would have to create a different class and add that class to your toggler, and then you could define a new class attribute in your CSS, which you can see at the bottom of the css with:
.dark-switch-long.isOn {
transform: translate(75px, -50%) !important;
}
Then if you were to add the class dark-switch-long
to your dark-toggle-thumb
element, then you would be able to extend the length to the specified pixels. However, I would suggest using my first example of jQuery CSS.
EDIT: As per OP's comment, they want the width to change dynamically. CSS will make this more challenging than using jQuery, so I created a function moveThumb()
that will slide the thumb to the right based on the width of the slider behind it. That function would look something like:
function moveThumb() {
let jumpLength = $('#dark-switch').outerWidth() - 25;
$('#dark-toggle-thumb').css("transform", "translate(" jumpLength "px, -50%)");
}
jumpLength
calculates the total distance that #dark-toggle-thumb
will have to move. Then, using jQuery CSS functionality, we can move that amount of pixels over. I updated the code snippet to show how this would work. This also includes adding a call to moveThumb()
when you use the ToggleWidth
button. Note that I used jQuery's hasClass
function to check when I should use moveThumb
.
function moveThumb() {
let jumpLength = $('#dark-switch').outerWidth() - 25;
$('#dark-toggle-thumb').css("transform", "translate(" jumpLength "px, -50%)");
}
$('#dark-switch').click(()=>{
$('#dark-toggle-thumb').toggleClass('isOn');
if ($('#dark-toggle-thumb').hasClass('isOn'))
moveThumb();
else
$('#dark-toggle-thumb').css("transform", "translate(6px, -50%)");
});
$('#toggleWidth').click(()=>{
$('#dark-switch').toggleClass('dark-switch-vary');
if ($('#dark-toggle-thumb').hasClass('isOn'))
moveThumb();
})
body {padding:2em}
.dark-switch-vary {
max-width:100px !important;
}
#dark-switch {
position:relative;
width:90%;
max-width:50px;
}
#dark-toggle-back {
width: calc(100% - 9px);
background: #666;
height: 24px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
border-radius: 99px;
}
#dark-toggle-thumb {
background: #ccc;
height: 17px;
border-radius: 99px;
width: 17px;
top: 50%;
transform: translate(6px, -50%);
position: absolute;
transition: .2s;
}
#dark-toggle-thumb svg {
width: 15px;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
/* TAKING OUT FOR jQuery EXAMPLE
.isOn {
transform: translate(26px, -50%) !important;
How do I make the translateX value flexible rather than a fixed number?
}
.dark-switch-long.isOn {
transform: translate(75px, -50%) !important;
}
*/
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="dark-switch">
<section id="dark-toggle-back"></section>
<section id="dark-toggle-thumb">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 3a7 7 0 0 0 7 9h2a9 9 0 1 1-9-9Z"></path></svg>
</section>
</div>
<br><br><br><br><br>
<button id="toggleWidth">ToggleWidth</button>
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
CodePudding user response:
The main problem – you can't translate (animated) between left:0 (right:inherit/auto) and right:0 (left:inherit/auto).
However you can fix this issue by appling a calc()
left offset.
I also recommend to prefer relative units like em.
$('#dark-switch').click(() => {
$('.dark-toggle-thumb').toggleClass('isOn');
});
$('#toggleWidth').click(() => {
$('#dark-switch').toggleClass('dark-switch-vary');
})
let fontSize = document.querySelector('#fontSize');
let darkSwitch = document.querySelector('#dark-switch');
fontSize.addEventListener('change', function(e){
let size = e.target.value;
darkSwitch.setAttribute('style', 'font-size:' 17*size 'px');
});
*{
box-sizing:border-box
}
body {
padding: 2em
}
:root{
--switchH: 17px;
--switchPadding: 0.2em;
}
.dark-switch {
position: relative;
width: 90%;
max-width: 3em;
font-size: var(--switchH);
line-height:1em;
}
.dark-toggle-back {
width: 100%;
background: #666;
height: calc(1em var(--switchPadding) *2 ) ;
position: relative;
border-radius: 1em;
}
.dark-toggle-thumb {
background: #ccc;
height: 1em;
width: 1em;
border-radius:100%;
position: absolute;
transition: 0.2s;
left:0;
margin: var(--switchPadding);
right: inherit;
}
.dark-toggle-thumb svg {
width: 1em;
height: 1em;
display:inline-block;
}
.dark-switch-vary {
max-width: 50vw;
}
.isOn {
left: calc(100% - calc(1em var(--switchPadding) *2 ) );
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="dark-switch" class="dark-switch">
<div class="dark-toggle-back">
<span class="dark-toggle-thumb">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 3a7 7 0 0 0 7 9h2a9 9 0 1 1-9-9Z"></path></svg>
</span>
</div>
</div>
<br><br><br><br><br>
<button id="toggleWidth">ToggleWidth</button>
<h3>Font size</h3>
<p>
<input id="fontSize" type="range" min="1" max="5" step="0.5" value="0" />
</p>
<iframe name="sif3" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
Jquery will only handle the css class toggling.