
How to prevent buttons from moving on click
March 2nd, 2020
- Link to How to prevent buttons from moving on click podcast on anchorfm
Recently I just finished a geo ip api
application as a project for the
JavaScript
course I am teaching, and I couldn’t get my buttons
to stop moving when I clicked them. I quickly figured out that it was
the disappearance of the browser window scrollbar that was
causing the buttons to move! When there was no data
rendered to the page, there was no overflow on the Y axis,
so by default, there was no scrollbar. But when I clicked on the
"Find me"
button that triggered the getGeoIp()
function, which
resulted in the appearance of the data on the page, the
scrollbar appeared. The toggling of the appearance
and
disappearance
of the browser window scrollbar was what caused
the buttons to move.
Setting the body element to
body {
overflow-y: scroll;
}
took care of the moving buttons issue.
Then I decided that I wanted to add the overflow-y
property to the
div
with the .wrapper
class, which contains the api
data
fetched from the Free IP Geolocation API
I am using in my
application called free-geo-ip
, because I like the idea of making it
easier for my users to scroll through a smaller area,
thereby making the actual height of the browser window
shorter. I love using inner scrollbars, especially when
rendering lists to a page in my applications.
But setting the body
to overflow-y: scroll
created a new issue!
First of all, I set an inner scrollbar
to my div
with the
.wrapper
class with the overflow-y
property set to "auto"
. However, I
also had the overflow-y
property on my body
element set to
scroll. This meant that initially, even though I was hiding
the wrapper div
and everything inside it, the scrollbar
of the
wrapper div
was NOT hidden in Chrome
.
As a result of this situation, I had to create some extra
conditions in my JavaScript
based on my CSS
styling in my
external stylesheet AND the CSS in JS
styling in my main.js
file, to
make sure that my inner scrollbar
did not initially appear when
there was no data
rendered to the page. The data
is only
rendered to the page when the Find me!
button is clicked.
At first, when I hid my div
with the .wrapper
class, the
scrollbar
still showed up locally in Safari
and Chrome
. It
did not show up in Firefox
. In Firefox
, the scrollbar
, along
with everything residing in the div
with the .wrapper
class, was
initially hidden.
This signaled to me that I would have to do some
cross-browser compatibility styling
. So I looked into the issue of
initially hiding the scrollbar
of the div
with the .wrapper
class when the page was supposed to be completely empty except for
the Find me!
button, the Refresh me!
button, and the site
footer. I found a great article on CSS Tricks
about styling
scrollbars
, which I have included in the Related Resources section
of this podcast
post.
First I did the following in my main.scss/main.css
file(s):
body {
/* So that there is always a browser window scrollbar to make sure that the find me and refresh me buttons do not move on click. */
overflow-y: scroll;
}
.wrapper {
/* Set a height to .wrapper div that is shorter than the actual wrapper div height when the data is rendered to the page, to ensure that there will always be overflow on the Y axis when the data renders to the page. */
height: 350px;
/* This is to make sure that a scrollbar appears when there is overflow on the Y axis. */
overflow-y: auto;
}
/* Scrollbar related styling */
/* Needed for scrollbar styling without -webkit pseudo-class to ensure that the scrollbar-thumb styling is applied to the .wrapper div */
.scrollbar-thumb {
background-color: #16174b;
outline: 1px solid slategrey;
border-radius: 15px;
}
/* end scrollbar styling without -webkit pseudo-class*/
/* Default/starting state of -webkit-scrollbar styling when there is no data on the page - hides the scrollbar */
.wrapper::-webkit-scrollbar {
width: 0px;
background: transparent;
}
/* .show-scrollbar class added when .backgroundSkyblue class added - shows the scrollbar for the .wrapper class */
.show-scrollbar::-webkit-scrollbar {
width: 1rem;
background: white;
}
/* ::-webkit-scrollbar-track-piece is the top-most layer of the progress bar not covered by the draggable scrolling element (thumb). */
.show-scrollbar::-webkit-scrollbar-track-piece {
box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.5);
}
/* The -webkit-scrollbar-track styling addressed the albeit small but empty apace below the scrollbar progress bar. */
.show-scrollbar::-webkit-scrollbar-track {
box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.5);
}
.show-scrollbar::-webkit-scrollbar-thumb {
background-color: #16174b;
outline: 1px solid slategrey;
border-radius: 15px;
}
/* the .backgroundSkyblue class added to div with .wrapper class in if/else statement if there is no .backgroundSkyblue class present. Causes switch from initial default orange background color to sky blue background color. And when the background is sky blue, the div.wrapper scrollbar appears. */
.backgroundSkyblue {
background: #8bd3fb;
}
All the above that I described now was created
after figuring out which classes
I needed to add with
which CSS
selectors in corresponding conditions, and
which classes I needed to remove in corresponding
conditions in my JavaScript
. Once I figured those logistics out,
then I could create the necessary CSS
styles in my external
stylesheet, main.scss/main.css
. I used node-sass
for this project!
I added the following JavaScript
in my main.js
file to make the
div
with the .wrapper
class AND its scrollbar
toggle from
visible
to invisible
based on certain conditions which
included CSS in JS
:
/* main.js Need to be able to query the body element, and the wrapper div with a class of ".wrapper". */
const body = document.querySelector('body')
const wrapper = document.querySelector('.wrapper')
Resulting if/else if/else
statement:
if (body.className !== 'backgroundSkyblue') {
body.classList.add('backgroundSkyblue')
wrapper.classList.add('show-scrollbar')
} else if (body.className === 'backgroundSkyblue') {
body.classList.remove('backgroundSkyblue')
body.style.backgroundColor = '#f08850'
wrapper.style.display = 'none'
wrapper.classList.remove('.show-scrollbar')
} else {
body.classList.add('backgroundSkyblue')
wrapper.style.display = 'block'
wrapper.classList.add('.show-scrollbar')
}
The initial if statement states that if the body
element does NOT
initially have a className
of backgroundSkyblue
(which it does NOT),
add the class backgroundSkyblue
to the body
element, and
add the class .show-scrollbar
to the div
with the class
.wrapper
.
The following else if
statement states that if
the body
element contains the className
backgroundSkyblue
, remove the
backgroundSkyblue
class from the body
element. Then add back
the initial background-color
of the body
element that is set
to the body
element in main.scss/main.css
. Why can this be
done? Because whenever you add CSS
styling via
JavaScript
, it overrides the values of the corresponding
css selector(s)
present in the external stylesheet
.
Since I wanted this condition to result in the hiding of the div
with the .wrapper
class and its scrollbar
, I added the display
property on the div
with the .wrapper
class dynamically
using the JavaScript
style
property, and setting its value
to none
. However, as explained earlier, I also had to
add browser-compatibility
styling to my CSS
. Therefore, I
had to remove the .show-scrollbar
class.
The (final) else
statement is what creates a toggle
between
initial invisibility
of the div
with the .wrapper
class and the
subsequent visibility
of the div
with the .wrapper
class.
Sometimes when you want to toggle
between one condition
and another
,
if the required conditions are more complex, an if/else
statement won’t necessarily suffice. You end up with an
if/else if/else
statement instead. That way you are able to
cover all your bases and create a circular condition
that
persists indefinitely! In this case, the else
statement is
saying, otherwise, just add the .backgroundSkyblue
class to
the div
with the .wrapper
class, set the display
property on the div
with the .wrapper
class to block
, because in the else/if
statement, I set the display
property of the div
with the
.wrapper
class to none
. Lastly, I needed to add back the
.show-scrollbar
class to the div
with the .wrapper
class, because
I added back the class .backgroundSkyblue
to it, signaling
that not only should the div
with the .wrapper
class be visible
,
but the scrollbar
for the div
with the .wrapper
class should be
visible
as well.
To view this application in action, please visit the
Free Geo IP
application on Github gh-pages
. To view the
source code
, please visit the geo-ip-app
repository on Github
.
I will be embedding this episode of Plugging in The Holes along with a transcript in the form of a post on interglobalmedianetwork.com for your hearing and reading pleasure. I will be including the related resource links mentioned in the podcast of course. Always do. Bye for now!
Related Resources

Created by Maria D. Campbell who lives and works in New York City building and teaching useful things. You should follow her on Twitter.