Bootstrap modal has a flaw - javascript

As I was learning bootstrap and trying out the example on the official page, I found a flaw (maybe) with the modal component.
Click the "Launch demo modal", you will notice there is a notable margin on the top right corner, and the navbar will stretch/shrink when the modal dialog disappear/appear.
Is that a bug or intentional? I think it's annoying, How to disable it?

To fix this manually simply add
body.modal-open,
.modal-open .navbar-fixed-top,
.modal-open .navbar-fixed-bottom
{
margin-right: 0px;
}
to a stylesheet that is applied after the bootstrap stylesheets.
If you want to hide the scrollbar as well you can add
.modal
{
overflow-y: auto;
}
as well.

this is the best solution i found:
body.modal-open, .modal-open .navbar-fixed-top, .modal-open .navbar-fixed-bottom {
padding-right: 0px !important;
overflow-y: auto;
}

This is a reported issue to bootstrap: https://github.com/twbs/bootstrap/issues/9855
And this is my temporary quick fix and it's work using fixed top navbar, only using javascript. Load this script along with your page.
$(document.body)
.on('show.bs.modal', function () {
if (this.clientHeight <= window.innerHeight) {
return;
}
// Get scrollbar width
var scrollbarWidth = getScrollBarWidth()
if (scrollbarWidth) {
$(document.body).css('padding-right', scrollbarWidth);
$('.navbar-fixed-top').css('padding-right', scrollbarWidth);
}
})
.on('hidden.bs.modal', function () {
$(document.body).css('padding-right', 0);
$('.navbar-fixed-top').css('padding-right', 0);
});
function getScrollBarWidth () {
var inner = document.createElement('p');
inner.style.width = "100%";
inner.style.height = "200px";
var outer = document.createElement('div');
outer.style.position = "absolute";
outer.style.top = "0px";
outer.style.left = "0px";
outer.style.visibility = "hidden";
outer.style.width = "200px";
outer.style.height = "150px";
outer.style.overflow = "hidden";
outer.appendChild (inner);
document.body.appendChild (outer);
var w1 = inner.offsetWidth;
outer.style.overflow = 'scroll';
var w2 = inner.offsetWidth;
if (w1 == w2) w2 = outer.clientWidth;
document.body.removeChild (outer);
return (w1 - w2);
};
Here is the working example: http://jsbin.com/oHiPIJi/64

Be sure to test for both when the original page already has a scrollbar and does not already have a scrollbar.
This worked for me with v3.1.1.
html, .modal, .modal.in, .modal-backdrop.in {
overflow-y: auto;
}

in addition to this also make sure you have the following
html { overflow-y:auto; }
in your stylesheet to stop it shifting left

I had this issue as well (bootstrap 3.1.1). I was opening a modal and there was a missing space on the backdrop (where a scroll bar will appear if the modal is greater than page height) and the content of the page was resizing and shifting to the left.
My layout uses a fixed navbar.
I added a couple of CSS selectors that seems to prevent the page resizing and ensuring that the modal-backdrop fills the screen
html {
/* This prevents the page from shifting when a modal is opened e.g. search */
overflow-y: auto;
}
.modal,.modal.in,.modal-backdrop.in {
/* These are to prevent the blank space for the scroll bar being displayed unless the modal is > page height */
overflow-y: auto;
}
I still find it a bit odd where you can have two scroll bars if the page and the modal content is more than the screen height but I can live with that.

body, .navbar-fixed-top, .navbar-fixed-bottom {
margin-right: 0 !important;
}
this worked for me

margin-right did not work in my case, I found padding-right to solve the issue.
body.modal-open {
padding-right: 0px;
}

I tried Agni Pradharma's fix, but had to slightly tweak it to make it work.
I got it working using this:
$(document.body)
.on('show.bs.modal', function () {
if (this.clientHeight <= window.innerHeight) {
return;
}
// Get scrollbar width
var scrollbarWidth = getScrollBarWidth()
if (scrollbarWidth) {
$('.navbar-fixed-top').css('margin-right', scrollbarWidth);
}
})
.on('hide.bs.modal', function () {
$('.navbar-fixed-top').css('margin-right', 0);
});
function getScrollBarWidth () {
var inner = document.createElement('p');
inner.style.width = "100%";
inner.style.height = "200px";
var outer = document.createElement('div');
outer.style.position = "absolute";
outer.style.top = "0px";
outer.style.left = "0px";
outer.style.visibility = "hidden";
outer.style.width = "200px";
outer.style.height = "150px";
outer.style.overflow = "hidden";
outer.appendChild (inner);
document.body.appendChild (outer);
var w1 = inner.offsetWidth;
outer.style.overflow = 'scroll';
var w2 = inner.offsetWidth;
if (w1 == w2) w2 = outer.clientWidth;
document.body.removeChild (outer);
return (w1 - w2);
};

Related

How to properly make a transition between relative and fixed HTML element?

On my simple website I have a horizontal navigation bar with several button there. When I scroll down I want it to be pinned on top.
For that I use a JS script which changes display type of the navigation bar from position:relative to position:fixed at a certain moment.
But the problem occurs with the website contents following the nav bar. Since elements with position:fixed are removed from the DOM flow and there is no created space for them, following contents displace from their position consequently being overflowed by the nav bar.
My navigation bar might change its shape due to wrap. It might also be moved down the page since upper horizontal elements may wrap too.
What the right way to do it? Is there any way to create space in the DOM for elements with position:fixed?
Main CSS:
main {
padding-top: 0;
margin-left: 20px;
margin-top: 0;
}
Navigation bar CSS:
.layout-nav {
display: flex;
flex-flow: wrap;
position: relative;
top: 0;
width: 100%;
padding: 10px 0 0;
margin-left: 10px;
background: #424242;
}
The JS script with a method which changes display type and padding on a certain scrollY which vary depending on the window width which leads to certain wrap:
let lastScrollY = 0;
let lastWidth = window.innerWidth;
let maxScrollPos = 105;
let ticking = false;
let fixed = false;
function modifyTitle(scrollPos, width) {
if (width > 773) {
maxScrollPos = 105;
} else {
maxScrollPos = 164;
}
if (scrollPos > maxScrollPos && !fixed) {
const topNav = document.querySelector('.layout-nav');
const main = document.querySelector('main');
topNav.style.position = "fixed";
topNav.style.padding = "23px 0 0";
main.style.paddingTop = "70px";
fixed = true;
} else if (scrollPos <= maxScrollPos && fixed) {
const topNav = document.querySelector('.layout-nav');
const main = document.querySelector('main');
topNav.style.position = "relative";
topNav.style.padding = "10px 0 0";
main.style.paddingTop = "0";
fixed = false;
}
}
document.addEventListener('scroll', (e) => {
lastScrollY = window.scrollY;
lastWidth = window.innerWidth;
if (!ticking) {
window.requestAnimationFrame(() => {
modifyTitle(lastScrollY, lastWidth);
ticking = false;
});
ticking = true;
}
});
So as of now I hardcoded some cases of wraps so that after navigation bar being pinned following content would be shown properly.
It is hard to hardcode every case.
And this solution seems not right at all for me since it is based not just on scrollY of the page but also on the width of the windows which indirectly causes certain wraps based on the resolution.

Stuttery CSS animation for enlarging images in Safari

I am trying to create an animation which takes a image that is anywhere on a page and moves it to the middle while resizing it to full width of the browser window. My solution works, but has some stutters/jumps in it, which I can't really explain. Is there anyone who has tried creating a similar animation already? EDIT: I noticed that the stutter problem only seems to appear in macOS Safari. In other browsers this animation appears to run perfectly smooth.
Here is my js code:
function getWindowWidth() {
return document.documentElement.clientWidth
}
function getWindowHeight() {
return document.documentElement.clientHeight;
}
//at the moment this is hacky and only supports one image to be enlarged
let en_img_left = null;
let en_img_top = null;
function enlargeImage(img) {
let boundingClientRect = img.getBoundingClientRect();
img.style.position = "fixed";
en_img_top = boundingClientRect.y + "px";
img.style.top = en_img_top;
en_img_left = boundingClientRect.x + "px";
img.style.left = en_img_left;
img.style.width = boundingClientRect.width + "px";
img.style.zIndex = "1000";
setTimeout(function() {
img.style.transition = "1s ease-in-out";
setTimeout(function() {
let scaleFactor = getWindowWidth() / boundingClientRect.width;
img.style.transform = "scale(" + scaleFactor + ")";
img.style.left = getWindowWidth() / 2 - (boundingClientRect.width / 2) + "px";
img.style.top = getWindowHeight() / 2 - boundingClientRect.height / 2 + "px";
}, 1);
}, 1);
return img;
}
function delargeImage(img) { //sorry for the function name
img.style.transition = "1s ease-in-out";
setTimeout(function() {
img.style.transform = "scale(1)";
img.style.left = en_img_left;
img.style.top = en_img_top;
}, 1);
return img;
}
example HTML+CSS code, but it can be any image with an ID on a website:
HTML:
<div class="container">
<img id="example" style="width: 100%" src="https://images.pexels.com/photos/1361815/pexels-photo-1361815.jpeg?cs=srgb&dl=blur-bokeh-close-up-1361815.jpg&fm=jpg">
</div>
CSS:
.container {
width: 200px;
}
I also made a jsfiddle displaying the stutter problem quite nicely:
https://jsfiddle.net/robske_110/vhz5Ln4o/11/
You are not using CSS animations or transitions!
The animation itself is executed through JavaScript in your example. Instead of computing every single step of an animation in JS and setting a new CSS property on each iteration, you should setup a CSS animation with the desired start- and end-states or define the properties, that should be transitioned. This way the animation should look smooth while transitioning.
Your example using a CSS transition (without any JS code):
.container {
width: 200px;
transition: width ease-in 1s;
}
.container:hover {
width: 80vw;
}
.container img {
width: 100%;
}
<div class="container">
<img id="example" src="https://images.pexels.com/photos/1361815/pexels-photo-1361815.jpeg?cs=srgb&dl=blur-bokeh-close-up-1361815.jpg&fm=jpg">
</div>

Fixed positioned element in overflow:scroll

I'm trying to make an element be always in top-right side of the window.
The problem is that when scrollbar appears, it keeps same position and ignores size of scrollbar.
How it looks now:
How I'd like it to look:
.outer {
position: relative;
height: 100px;
overflow-x: hidden;
overflow-y: auto;
width: 100%;
}
.icon {
position: fixed;
right: 0;
top: 0;
background: red;
width: 10px; height: 10px;
}
​
The fiddle: http://jsfiddle.net/L5YhF/
Are there any hacks how to fix it?
Thank you.
Try making your right attribute:
right: 10px;
or whatever offset you need.
EDIT :
According to aswer to this this question How can I get the browser's scrollbar sizes? you can write a javascript function to put place your icon the way you want in a cross-browser manner. Example in this fiddle:
http://jsfiddle.net/L5YhF/9/
Code:
function getScrollBarWidth () {
var inner = document.createElement('p');
inner.style.width = "100%";
inner.style.height = "200px";
var outer = document.createElement('div');
outer.style.position = "absolute";
outer.style.top = "0px";
outer.style.left = "0px";
outer.style.visibility = "hidden";
outer.style.width = "200px";
outer.style.height = "150px";
outer.style.overflow = "hidden";
outer.appendChild (inner);
document.body.appendChild (outer);
var w1 = inner.offsetWidth;
outer.style.overflow = 'scroll';
var w2 = inner.offsetWidth;
if (w1 == w2) w2 = outer.clientWidth;
document.body.removeChild (outer);
return (w1 - w2);
};
window.onload = function() {
var scrollWidth = getScrollBarWidth ();
var ico = document.getElementById('ico');
ico.style.right = scrollWidth + "px";
};

finding the DOM scrollbar width

I would like to find the pixel width of the vertical and horizontal scrollbars.
I know that they are different for different OSes/browsers.
I found this code that attempts to detect it, but alas, it doesnt seem to work on IE7:
function scrollbarWidth() {
var scrollbarWidth = 0;
if ($.browser.msie) {
var $textarea1 = $('<textarea cols="10" rows="2"></textarea>')
.css({ position: 'absolute', top: -1000, left: -1000 }).appendTo('body'),
$textarea2 = $('<textarea cols="10" rows="2" style="overflow: hidden;"></textarea>')
.css({ position: 'absolute', top: -1000, left: -1000 }).appendTo('body');
scrollbarWidth = $textarea1.width() - $textarea2.width() + 2; // + 2 for border offset
$textarea1.add($textarea2).remove();
} else {
var $div = $('<div />')
.css({ width: 100, height: 100, overflow: 'auto', position: 'absolute', top: -1000, left: -1000 })
.prependTo('body').append('<div />').find('div')
.css({ width: '100%', height: 200 });
scrollbarWidth = 100 - $div.width();
$div.parent().remove();
}
return scrollbarWidth;
}
This function should give you the width of the vertical scrollbar:
function scrollbarWidth()
{
var outer = document.createElement("div");
outer.style.visibility = "hidden";
outer.style.width = "100px";
// for win 8
outer.style.msOverflowStyle = "scrollbar";
document.body.appendChild(outer);
var widthNoScroll = outer.offsetWidth;
// force scrollbars
outer.style.overflow = "scroll";
// add innerdiv
var inner = document.createElement("div");
inner.style.width = "100%";
outer.appendChild(inner);
var widthWithScroll = inner.offsetWidth;
// remove divs
outer.parentNode.removeChild(outer);
return widthNoScroll - widthWithScroll;
}
The main steps of this function are the following:
Create an outer div of width 100px
Then forces the scrollbar to appear in the outer div
Create a new inner div and append inside the outer div. Set its height to 100%
Calculate the difference between both widths.
Similarly, you can also get both the width of the vertical scrollbar, and the height of the horizontal scrollbar, setting a given height to the outer div, and calculating also the height difference of both divs, like this:
function scrollbarWidthHeight()
{
var outer = document.createElement("div");
outer.style.visibility = "hidden";
outer.style.width = "100px";
outer.style.height = "100px";
// for win 8
outer.style.msOverflowStyle = "scrollbar";
document.body.appendChild(outer);
var widthNoScroll = outer.offsetWidth;
var heightNoScroll = outer.offsetHeight;
// force scrollbars
outer.style.overflow = "scroll";
// add innerdiv
var inner = document.createElement("div");
inner.style.width = "100%";
inner.style.height = "100%";
outer.appendChild(inner);
var widthWithScroll = inner.offsetWidth;
var heightWithScroll = inner.offsetHeight;
// remove divs
outer.parentNode.removeChild(outer);
return {
width: widthNoScroll - widthWithScroll,
height: heightNoScroll - heightWithScroll
};
}
Tested in chrome, firefox, IE6, IE8, and safari.
It also uses native JavaScript (DOM functions), and doesn't use external dependencies like jQuery :)
Why are you trying to do this? Some context would be nice. If you're trying to make custom theme-able scrollbars, there are many scripts to do this, a good one being jQuery Scrollbars.

Sliding Div to auto height

I have a div that I slide in and out.
To do this I just increase the height by 2px every second until a preset height from a height of 0.
Is there anyway to determine the content height of the div as the content is unpredictible height considering the starting properties of the div are display:none and height:0?
Thank you.
The trick is to temporarily show it, measure the height, then hide it again. And if you use visibility: hidden and position: absolute, it won't change the page layout while you do it.
function getElementHeight(el)
{
var styles = {
visibility: el.style.visibility,
height: el.style.height,
position: el.style.position,
display: el.style.display
};
el.style.visibility = "hidden";
el.style.height = "auto";
el.style.position = "absolute";
el.style.display = "block";
var height = el.offsetHeight;
el.style.display = styles.display;
el.style.position = styles.position;
el.style.height = styles.height;
el.style.visibility = styles.visibility;
return height;
}
If you want to get what the style height should be, you can add these two lines after var height = el.offsetHeight;:
el.style.height = height + "px";
height += (height - el.offsetHeight);

Categories

Resources