Lower the margin by time when using slideToggle in Jquery - javascript

I have this example
$(document).ready(function() {
$("#btn").click(function() {
removeSecondContainer();
});
});
function removeSecondContainer() {
var container = $("#c2");
container.slideToggle(500, function() {
container.remove();
});
}
.container {
margin: 20px auto;
height: 20px;
width: 100px;
background: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="btn">Remove the second container</button>
<div class="container" id="c1">
</div>
<div class="container" id="c2">
</div>
<div class="container" id="c3">
</div>
As you can see, the process is not smooth at all. When the second container gets removed, the margin of the second container gets removed too. This causes a pull from the top.
How can I get this smoothly? I thought about lowering the margin by time to 0 when removing the container.

You are facing a margin collapsing issue. As you may notice you don't have 40px between each container like expected but only 20px.
As you can read here:
In CSS, the adjoining margins of two or more boxes (which might or
might not be siblings) can combine to form a single margin. Margins
that combine this way are said to collapse, and the resulting combined
margin is called a collapsed margin.
So when removing the element you decrease both margin at the top and bottom to leave the maring of the first and last element. And when the height of the element reaches 0 and get removed, you create another margin collapsing between the remaining block and thus the jump from 40px to 20px of margin.
And idea to avoid this is to increase height and use linear-gradient to color only the part you want (and leave transparent the part previously used for the margin). Like that the transition will go smoothly as there is no more margin issue.
$(document).ready(function() {
$("#btn").click(function() {
removeSecondContainer();
});
});
function removeSecondContainer() {
var container = $("#c2");
container.slideToggle(500, function() {
container.remove();
});
}
.container {
margin: auto;
height: 60px;
width: 100px;
background: linear-gradient(to bottom, transparent 33.33%, red 33.33%, red 66.67%, transparent 66.67%);/
box-sizing: border-box;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="btn">Remove the second container</button>
<div class="container" id="c1">
</div>
<div class="container" id="c2">
</div>
<div class="container" id="c3">
</div>
Or use flex by adding another container as there is no margin collpasing with flexbox (Margin collapsing in flexbox):
$(document).ready(function() {
$("#btn").click(function() {
removeSecondContainer();
});
});
function removeSecondContainer() {
var container = $("#c2");
container.slideToggle(500, function() {
container.remove();
});
}
.box {
display: flex;
flex-direction: column;
}
.container {
margin: 20px auto;
height: 20px;
width: 100px;
background: red;
box-sizing: border-box;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="btn">Remove the second container</button>
<div class="box">
<div class="container" id="c1">
</div>
<div class="container" id="c2">
</div>
<div class="container" id="c3">
</div>
</div>

Its due to clearing. The bottom margin is not working well with them. Either use float or remove margin-bottom or margin-top to 0
here is Example
edit: update with remove div
https://jsfiddle.net/f5zw18er/3/

You could potentially use jQuery animate with a callback function. It's slightly different because it doesn't include the slide toggle, but in your example it's only being removed, so this could work.
Here we're removing the margin and also hiding the element with the animation, and then finally removing the element in the callback.
$(document).ready(function() {
$("#btn").click(function() {
removeSecondContainer();
});
});
function removeSecondContainer() {
var container = $("#c2");
container.animate({ 'margin' : '-20px auto', 'opacity': 0 }, 500, function(){
container.remove();
});
}
.container {
margin: 20px auto;
height: 20px;
width: 100px;
background: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="btn">Remove the second container</button>
<div class="container" id="c1">
</div>
<div class="container" id="c2">
</div>
<div class="container" id="c3">
</div>

Related

Moving an element smoothly to its new position without manually setting its position

My apologies if the subject is a bit non-descriptive. I'm having a hard time trying to explain what I'm trying to achieve in a one-liner.
But in a few sentences: I'm trying to have an element, a DIV in this case, move smoothly to its new position. But the caveat is that I'm not setting its position manually. It receives a new position because I'm removing other DIVs from the page flow.
<!DOCTYPE html>
<html>
<head>
<style>
.block {
width: 200px;
height: 20px;
border: 1px solid black;
margin: 20px;
}
</style>
<script>
function removeBlock() {
document.getElementById("block2").style.display = "none";
}
</script>
</head>
<body>
<div id="block1" class="block">
This is block 1
</div>
<div id="block2" class="block">
This is block 2
</div>
<div id="block3" class="block">
This is block 3
</div>
<button type="button" onclick="removeBlock();">
Remove block 2
</button>
</body>
</html>
Here's the fiddle: https://jsfiddle.net/nfhycrkL/
If you click the button, Block 2 is hidden and Block 3 moves up. I want this move to be smooth. Is this at all possible? I don't want to use absolute positioning since the page is responsive and the position of the DIVs are depending on the page size.
Try This Solution
function removeBlock()
{
document.getElementById("block2").style.height = "0px";
document.getElementById("block2").style.margin = "0px";
document.getElementById("block2").style.borderWidth = "0px";
document.getElementById("block2").style.fontSize = "0px";
}
.block {
width: 200px;
height: 20px;
border: 1px solid black;
margin: 20px;
}
#block2 {
transition:all 0.5s linear;
}
<div id="block1" class="block">
This is block 1
</div>
<div id="block2" class="block">
This is block 2
</div>
<div id="block3" class="block">
This is block 3
</div>
<button type="button" onclick="removeBlock();">
Remove block 2
</button>
You could add a new class to an element with javascript that you want to hide and do css transition.
Here's a small example with remove and toggle options https://jsfiddle.net/nfhycrkL/9/
html:
<div id="block1" class="block">
This is block 1
</div>
<div id="block2" class="block">
This is block 2
</div>
<div id="block3" class="block">
This is block 3
</div>
<button type="button" onclick="toggleBlock();">
Toggle block 2
</button>
<button type="button" onclick="removeBlock();">
Remove block 2
</button>
js :
function toggleBlock() {
document.getElementById("block2").classList.toggle('block-hidden')
}
function removeBlock() {
document.getElementById("block2").classList.add('block-hidden')
}
css:
.block {
width: 200px;
height: 20px;
border: 1px solid black;
margin: 0 20px 20px;
overflow:hidden;
transition: all .25s;
}
.block-hidden {
height: 0px;
margin: 0px;
border: none;
}
$(document).ready(function(){
$("button").click(function(){
//document.getElementById("block2").style.display = "none";
$("#block2").fadeOut(1000);
});
});
.block {
width: 200px;
height: 20px;
border: 1px solid black;
margin: 20px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.3/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.css" rel="stylesheet"/>
<div id="block1" class="block">
This is block 1
</div>
<div id="block2" class="block">
This is block 2
</div>
<div id="block3" class="block">
This is block 3
</div>
<button type="button">
Remove block 2
</button>
use Jquery effects. I hope this helps.
Here's a simple example in vanillaJS with a CSS transition
Jsfiddle demo
Update your style adding a transition for the .block element
CSS
.block {
width: 200px;
height: 20px;
border: 1px solid black;
margin: 20px 20px 0;
max-height: 500px;
transition: opacity 0s 0s, margin .25s 0s, max-height .25s 0s;
}
.removedBlock {
box-sizing: border-box;
opacity: 0;
margin: 0;
max-height: 0;
}
so that the function can trigger a max-height animation by adding the removedBlock class
<div id="block1" class="block">
This is block 1
</div>
<div id="block2" class="block">
This is block 2
</div>
<div id="block3" class="block">
This is block 3
</div>
<button type="button" onclick="removeBlock('block2');">
Remove block 2
</button>
JS
function removeBlock(id) {
var block = document.getElementById(id);
block.classList.add('removedBlock');
}
When you do a removal, the element disappears due the opacity set to 0, then margin and max-height will make the block collapsing.
Note that since a transition can't be triggered to/from an auto value I've set a huge starting max-height for this purpose. If you want to see a smoother transition either change that property with a lower value or simply increase the duration of the transition.
A more refined version could instead get the height of the element before applying the transition e.g.
function removeBlock(id) {
var block = document.getElementById(id);
var blockHeight = block.offsetHeight;
block.style.height = blockHeight + 'px';
block.classList.add('removedBlock');
}
so the style becomes
.block {
width: 200px;
height: 20px;
border: 1px solid black;
margin: 20px 20px 0;
transition: opacity 0s 0s, margin .5s 0s, height .5s 0s;
}
.removedBlock {
box-sizing: border-box;
opacity: 0;
margin: 0;
height: 0 !important;
}
JsFiddle
Thanks everybody for your answers! And although all of them work somewhat, they do not work as soon as the layout becomes more complex, or if you try to hide/show more/other objects.
So I spend the past few hours creating a Javascript solution that I think will work in any situation (and on any browser too).
In short, how it works is that you "mark" as many elements as you like to be hidden/shown with the SetDisplay() function (see the first button). Once that has been done, you call the same SetDisplay function without any parameters and see the magic happen! The Javascript actually quickly removes the elements and let the page reflow (all invisible to the viewer). It then examines the new positions, reinserts the elements to hide and move all other elements to their new position by setting style.transition and by using position:relative and new top and left values. Once it's done with the transition, it hides the elements permanently, resets all changed style values and let the page reflow again.
SetDisplay( "block2", "none" );
SetDisplay( "block3", "none" );
SetDisplay( "block4", "none" );
SetDisplay();
You can reinsert elements the same way (the second button).
SetDisplay( "block2", "" );
SetDisplay();
https://jsfiddle.net/yq7xor5j/3/
(Edit: made a change to the fiddle to correct a small bug)

Scroll to bottom of hidden div?

I have a simple chat JS application, with a div.chat-holder holding all chat messages within a pane on the overall window. I set height of '.chat-holder so it remains fixed in size, and allows for scrolling of all the messages.
<style>
.chat-holder {
height: 30px;
overflow-y: scroll;
}
</style>
<div class="pane">
<div class="chat-holder">
<div class="chat-item">
first msg
</div>
<div class="chat-item">
second msg
</div>
....
<div class="chat-item">
last msg
</div>
</div>
</div>
On page load, I scroll to the bottom by setting the scrollTop of the holder:
var $holder = $('.chat-holder');
$holder.scrollTop($holder[0].scrollHeight);
and this works fine.
Problem occurs when I start with div.pane set to display:none. Ideally, I look to have a separate event to "show/hide" the chat pane, and start with the pane hidden.
When the parent pane is hidden, the .chat-holder scrollHeight is 0, so on load, the hidden pane won't be scrolled to the bottom. Which means when the pane is displayed, the chats are not scrolled to the most recent chats. You can see this in the following snippet: with .pane initially not displayed, scroll isn't set. If you set .pane to start displayed, then scroll works fine.
Is there anyway to "scroll to the bottom" while parent is hidden? (Yes, I know I could do this by detecting when the chat-holder is exposed & then scroll to the bottom, but I'm looking to do it on load.)
$(function() {
var $holder = $('.chat-holder');
$holder.scrollTop($holder[0].scrollHeight);
$('button').click(function() {
$('.pane').toggleClass('active');
});
});
.chat-holder {
height: 30px;
overflow-y: scroll;
border: thin solid black;
}
.chat-item {
font-size: 20px;
}
.pane {
display: none;
}
.pane.active {
display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="pane">
<div class="chat-holder">
<div class="chat-item">first msg</div>
<div class="chat-item">second msg</div>
<div class="chat-item">last msg</div>
</div>
</div>
<button>Toggle pane</button>
You can get creative and use opacity or visibility rules instead of display: none:
$(function() {
var $holder = $('.chat-holder');
$holder.scrollTop($holder[0].scrollHeight);
$('button').click(function() {
$('.pane').toggleClass('active');
});
});
.chat-holder {
height: 30px;
overflow-y: scroll;
border: thin solid black;
}
.chat-item {
font-size: 20px;
}
.pane {
opacity: 0;
position: absolute;
z-index: 1;
}
.pane.active {
opacity: 1;
position: relative;
}
button {
z-index: 2;
position: relative;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="pane">
<div class="chat-holder">
<div class="chat-item">first msg</div>
<div class="chat-item">second msg</div>
<div class="chat-item">last msg</div>
</div>
</div>
<button>Toggle pane</button>

Margin issue with jquery load()

I am loading html page inside a div with jquery. It does work fine.
var loginBtn = $("#loginBtn");
var loginPage = $("#login");
var submitBtn = $("#submitBtn");
var submitPage = $("#submit");
var checkBtn = $("#checkBtn");
var checkPage = $("#check");
loginPage.load( "login.html" );
submitPage.load( "submitPoints.html" );
checkPage.load( "checkPoints.html" );
body {
margin: 0 !important;
padding: 0 !important;
background-color: white;
}
#mainFrame {
width: 100%;
height: 300px;
background-color:cadetblue;
padding-top: 0;
margin-top: 0px;
position: relative;
}
<div id="mainFrame">
<div id="login"></div>
<div id="check"></div>
<div id="submit"></div>
</div>
My issue is that if the loaded html has no content, the margin between the parent document body (white) and the top of the loaded html (green) is none (that's what I want, it's ok).
However as soon as I add content to the loaded html, a gap is generated at the top of the page :\
I thought it was all about setting some line-height prop in the css but it seems helpless.
Any ideas what I am doing wrong ?
What you are seeing is the top margin of the first piece of content overflowing its container (also known more commonly as margin collapsing):
body {
background:yellow;
}
#container {
background:green;
height:300px;
}
<div id="container">
<h1>I have a top margin of 1em by default that is overflowing into the body.</h1>
</div>
If you give your container element a padding of that same amount, the margin space of the body won't be used and the element will be pushed down in the green area.
body {
background:yellow;
}
#container {
background:green;
height:300px;
padding:1em;
}
<div id="container">
<h1>I have a top margin of 1em by default that is now contained within my parent.</h1>
</div>
Or, you could set the top margin of the first piece of content to zero:
body {
background:yellow;
}
#container {
background:green;
height:300px;
}
#container > h1:first-child { margin-top:0; }
<div id="container">
<h1>My top margin has been set to zero.</h1>
</div>
Finally, you could set the overflow of the content area to auto but (although this seems to be the popular answer), I don't prefer this approach as you run the risk of unintended fitting of the content as the content changes and/or the container size changes. You give up a bit of sizing control:
body {
background:yellow;
}
#container {
background:green;
height:300px;
overflow:auto;
}
<div id="container">
<h1>The content area has had its overflow set to auto.</h1>
</div>
When you load new content it gets rendered in the document and those new elements might have properties. In this case, most probably the Login has a margin value. Another option is that it has a class or some selector that is being picked up by a CSS file which appends the margin to it.
Easiet way would be to right-click on the Login element, choose inspect, and analyze the style of the element with web-dev / style.
If you want to keep the margin on the inner content, you should set an overflow. Look what happens when we remove the overflow: auto line from .content > div (try clicking the box after running the code sample below).
This is because of margin collapsing. The margin on the inner content is combined with the margin on the outer element and applied on the outer element, i.e. two margins of the two elements are collapsed into a single margin.
document.querySelector('.content').addEventListener('click', (e) => {
e.target.classList.toggle('overflow');
});
html,
body {
margin: 0;
padding: 0;
}
.outer {
width: 200px;
background: red;
}
.content > div {
width: 100%;
height: 300px;
background: cadetblue;
cursor: pointer;
}
.content > div.overflow {
overflow: auto;
}
.test {
margin: 10px;
display: block;
}
<div class="outer">
<div class="content">
<div><span class="test">Test</span></div>
</div>
</div>

JQuery Hide/Show on Scroll down

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.

Slide div (left to right) from 'behind' a button using jQuery

Using the last example of this guide (http://www.learningjquery.com/2009/02/slide-elements-in-different-directions), I am trying to get a div to toggle 'behind' a button.
Desired Result Example:
Initially the div including input fields, Button B, and Button C will be hidden. When 'Slide it' button is clicked, the div slides in FROM behind the button until a specific distance (margin) is met between the RIGHT side of the div and the 'Slide it' button.
Upon clicking the button again (and/or when 'Esc' key is pressed), the div will slide "into" the button.
Here is the script, followed by the demo:
$('#button1').click(function() {
var $marginRighty = $('.inner');
$marginRighty.animate({
marginRight: parseInt(
$marginRighty.css('marginRight'),10) == 0 ?
$marginRighty.outerWidth() : 0});
});
https://jsfiddle.net/ishq786/xqb5a7dq/
Fiddle: https://jsfiddle.net/xqb5a7dq/6/
$('#button1').on('click', function() {
animateDiv();
})
$(document).keyup(function(e) {
if (e.keyCode === 27 && $('.inner').hasClass('visible')) {
animateDiv();
}
})
function animateDiv () {
$('.inner').toggleClass('visible');
$('.inner').animate({
width: 'toggle',
},350);
}
.toolbar {
width: 100%;
display: inline-block;
overflow: hidden;
white-space: nowrap;
margin: 0px auto;
padding: 5px 0px;
background-color: skyblue;
}
#divA,
#divB {
display: inline;
}
.inner {
float: right;
display: none;
padding-right: 20px;
}
#button1 {
float: right;
margin-right: 5px;
}
input,
button {
padding: 5px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class='toolbar'>
<div>
<button id="button1">Slide it</button>
</div>
<div class="inner is-hidden">
<div id="divA">
<input type="text" />
<input type="password" />
</div>
<div id="divB">
<button>Button B</button>
<button>Button C</button>
</div>
</div>
</div>
Fiddle.
I've solved some of your problems. I confess I'm not exactly sure how you want the outcome to look so I'll leave the tweaking to you. But two main things;
I have used css to get one on top of the other. I positioned the inner div 100% right, position: relative, and the button over the top now has its parent div set from the right with position: absolute so that it can overlap with no footprint. I literally moved the button lower in the HTML so I didn't have to mess about with z-index, haha.
I moved your Javascript into a separate function so you can call that with a keypress or however you like. This means you won't have code duplication.

Categories