Javascript only on scroll events? - javascript

Is it possible to create a minimalist javasript only on-scroll function to hide my menu bar, so only the menu button shows and the button itself gains a white backgroud colour? I have been looking into this and I believed to have the code fairly down. But I am very new to javasript and cannot fully understand the syntax of it yet. Below is what I have now in a jsfiddle:
https://jsfiddle.net/AngusBerry/zLt0yLou/2/#&togetherjs=Vsth32pa6L
html:
<!DOCTYPE html>
<body>
<header>
<h1><span id="tstscroll">0</span></h1>
<div class="MenuButton" id="mobMenu"></div>
<!--<p>as you can see, this is the header for the website. Here will also be contained all of the links to anywhere on the support system. this and the footer will both be FIXED and will move with the page.</p>-->
</header>
</body>
css:
header {
top: 0px;
position: fixed;
max-height: 100px;
width: 100%;
padding-top: 2px;
padding-bottom: 3.5px;
color: green;
animation: max-height-header;
animation-duration: 1.5s;
}
header h1 {
position: relative;
float: left;
margin-left: 3px;
}
header .MenuButton {
width: 28px;
height: 6px;
border-top: 6px solid;
border-bottom: 18px double;
margin-right: 5px;
margin-top: 2px;
}
javascript:
var mobilemenu = document.getElementById('mobMenu');
var testscroller = document.getElementById('tstscroll');
var x = 0;
document.mobilemenu.addEventListener("scroll", menuScrolMob);
function menuScrolMob(mobilemenu.onscroll) {
testscroller.innerhtml = x += 1;
}

You'll need to run that script either last in your body, or after page been loaded, or else it won't be able to access the elements.
Also, your script code is wrong, so here is a solution showing how to solve both those issues
(function(w, d) { /* this is a closure and will keep its variables
from polluting the global namespace and it also
declare 2 variables (w, d) to be used inside it */
w.addEventListener("load", function() {
var mobilemenu = d.getElementById('mobMenu');
var testscroller = d.getElementById('tstscroll');
var x = 0;
mobilemenu.addEventListener("scroll", function() {
testscroller.innerhtml = x += 1;
});
});
}(window, document)); /* here I pass the window and document object into the
closure's variables (w, d) to make the code slimmer */

Related

How to change the scrollbar styling via javaScript

I have implemented a customized scrollbar (code is provided below).
I want to use the javaScript event "onScroll" to change the scrollbar thumb styling while scrolling, but I don't know the right way to do so.
Is there a way to access the scrollbar style, perhaps as a JavaScript object, i.e.:
Container.style.-webkit-scrollbar-thumb.backgroundColor = 'black';?
Here is some code to demonstrate how my scrollbar is implemented:
CSS:
#container::-webkit-scrollbar {
width: 10vw;
}
#container::-webkit-scrollbar-thumb {
background-color: grey;
border-radius: 50px;
border: 5px solid black;
}
#container::-webkit-scrollbar-thumb:hover {
border: 2px solid black;
background-color: grey;
}
#container::-webkit-scrollbar-track {
background-color: black;
border-bottom-left-radius: 12px;
border-top-left-radius: 12px;
}
JavaScript:
elementsContainer.addEventListener("scroll", function wheelStyle() {
//elementsContainer.WHAT??
});
Here is my solution:
The idea is to create a CSS stylesheet rule dynamically and update it while scrolling.
Here is the snippet I used to test in stackoverflow itself (by running it from the console directly):
// Based on https://stackoverflow.com/a/31126328/1941313
appendRule = (sheet) => {
console.log({sheet});
const len = sheet.cssRules.length;
sheet.insertRule('body{}', len);
return sheet.cssRules[len];
}
ruleForScroll = appendRule(Array.from(document.styleSheets).slice(-1)[0]);
randomColor = () => Math.floor(255 * Math.random());
component = document.querySelector('.left-sidebar--sticky-container.js-sticky-leftnav');
component.addEventListener("scroll", function wheelStyle() {
ruleForScroll.selectorText = '.left-sidebar--sticky-container.js-sticky-leftnav::-webkit-scrollbar-track';
ruleForScroll.style["background"] = `rgb(${randomColor()},${randomColor()},${randomColor()})`;
});
This specifically affects the side menu of stackoverflow, changing the scrollbar's color randomly while scrolling.
Here is an independent solution in a CodePen. Note that an important prerequisite for the style to apply is the following css rule:
.test::-webkit-scrollbar {
width: 10px;
height: 10px;
background-color: transparent;
}

Automatic Resizing Function

So I have a bar that contains many smaller divs inside it, each with the same class.
.outer-bar{
width: 100%;
height: 50px;
background-color: blue;
}
.outer-bar button {
width: 50px;
height: 40px;
outline: none;
background-color: white;
border: none;
margin-top: 5px;
margin-bottom: 5px;
float: left;
margin-left: 2px;
}
<div class = "outer-bar">
<button class = "a">Hi</button>
<button class = "a">Hi</button>
<button class = "a">Hi</button>
<button class = "a">Hi</button>
<button class = "a">Hi</button>
<button class = "a">Hi</button>
<button class = "a">Hi</button>
<button class = "a">Hi</button>
<button class = "a">Hi</button>
</div>
When the window resized, I want the window to only show enough of them that there is no overflow, but there is as many as there can be.
So using: window.addEventListener("resize", function() {} );, is there any way to implement that?
One great example I saw was on Google Docs, where their bar automatically adjusts.
I would prefer doing this with Javascript and CSS and no Jquery or external libaries. (Note that I simplified the widths and number of elements for illustration purposes although the basic idea is still the same). I also prefer the use of for loops and document.getElementsByClassName("a").
I already started it by using:
var ribbon = document.getElementsByClassName("outer-bar")[0];
var prevChild = 0;
for(var i = 0; i < ribbon.children.length; i++) {
if(ribbon.children[i].getBoundingClientRect().right < prevChild) {
for(var c = i; c < ribbon.children.length; c++) {
ribbon.children[c].style.display = "none";
}
break;
}
prevChild = ribbon.children[i].getBoundingClientRect().right;
}
Although that method does not make the children displayable once I resize the window to be bigger.
If you know ribbon length why not divide it by the size you want the button to be. Then loop through the buttons making the first ones(ribbon length/size) block and the rest none.
I am not sure I understand you right, but it looks like you need to do a different styling to the inner divs when the screen resizes. CSS is capable of handling this for you without needing to use JavaScript at all.
Media Queries let you specify different styling based on the current size of the window.
.outer-bar button {
width: 50px;
height: 40px;
outline: none;
background-color: white;
border: none;
margin-top: 5px;
margin-bottom: 5px;
float: left;
margin-left: 2px;
}
#media screen and (max-width: 600px) {
.outer-bar button {
/*
Specify here any different styling
Ex.
*/
width: 25px;
}
}

JS code to change parameter 'top' on click

Here is the pen I've created.
HTML
<div class = 'cc'>
<div class = 'bb'><div class = 'aa'> Some word </div></div>
</div>
CSS
.cc {
width: 100%;
min-height: 90px;
margin: 0;
padding: 0;
border: 1px solid #999999;
border-radius: 3px;
padding-left: 20px;
padding-right: 20px;
font-family: "Calibri";
font-size: 17px;
color: #666666;
background-color: rgba(0,0,0,.0);
}
.bb {
height: 100px;
width: 100%;
background-color: rgba(0,0,0,.5);
}
.aa {
position: relative;
top: 50%;
transform: translateY(-50%);
}
Now I want to create a clickable event such that when user click on class bb, page will check the top parameter of class aa - if it is 50% then smoothly change that to 10% and vice versa.
I want to use JavaScript code to achieve that. How can I do that?
hey just tried to gave shot at it , seems its working please look into this
let bb = document.querySelector('.bb');
let aa = document.querySelector('.aa');
bb.addEventListener('click',e => {
let top = window.getComputedStyle(aa).getPropertyValue('top');
if(top === '50px'){
aa.style.top = '10%';
}else{
aa.style.top = '50%';
}
})
Got it. It is tested and it seems to work.
let bb = document.querySelector('.bb');
let aa = document.querySelector('.aa');
bb.addEventListener('click', function(){
if(window.getComputedStyle(aa).getPropertyValue('top') === '50px'){
aa.style.top = '10%';
}else{
aa.style.top = '50%';
}
})
First, I used querySelector to get .bb and .aa.
Then, I added a event listener to bb.
Next, in the event listener I used window.getComputedStyle(), got the value of top from it and checked if it is 50px.
Last of all, if it is, change that to 10%, else change it to 50%.
I did this on CodePen, you can check it here (notice I changed the style from gray to white because gray is hard to read inside a black box).

Javascript causing mobile navigation to require a 'double tap for links to work

I've noticed on the mobile version (iOS) of my website that the main navigation requires links to be tapped twice for the page to redirect. After removing various styles/bits of code I found the cause of the problem, it's my Javascript for a 'sliding line' hover effect.
My basic understanding would be that as the script is still running on mobile, when it's not really needed, it means the navigation is running/expecting a hover effect and once that's run you can then click a link as you intend?
The script works perfect on desktop, so I don't want to change any of the functionality but is there something I can add to prevent this bug on mobile devices? Alternatively, would a javascript 'media query' type thing, stopping the script from running below 1000px be a better solution? If so what would be the best way to implement that?
Thank in advance!
CodePen: https://codepen.io/moy/pen/pZdjMX
$(function() {
var $el,
leftPos,
newWidth,
$mainNav = $(".site-nav__list");
$mainNav.append("<div class='site-nav__line'></div>");
var $magicLine = $(".site-nav__line"),
$currentMenu = $(".current-menu-item");
$magicLine
.width($currentMenu.length ? $currentMenu.width() : 0)
.css("left", $currentMenu.length ? $currentMenu.find("a").position().left : 0)
.data("origLeft", $magicLine.position().left)
.data("origWidth", $magicLine.width());
var hoverOut;
$(".site-nav__list li a").hover(function() {
clearTimeout(hoverOut);
$el = $(this);
leftPos = $el.position().left;
newWidth = $el.parent().width();
if (!$magicLine.width()) {
$magicLine.stop().hide().css({
left: leftPos,
width: newWidth
}).fadeIn(100);
} else {
$magicLine.stop().animate({
opacity: 1,
left: leftPos,
width: newWidth
});
}
},
function() {
hoverOut = setTimeout(function() {
if (!$currentMenu.length) {
$magicLine.fadeOut(100, function() {
$magicLine.css({
left: $magicLine.data("origLeft"),
width: $magicLine.data("origWidth")
});
});
} else {
$magicLine.stop().animate({
left: $magicLine.data("origLeft"),
width: $magicLine.data("origWidth")
});
}
}, 100);
}
);
});
/* Header */
.page-head {
background: white;
border-top: 2px solid #ddd;
box-sizing: border-box;
overflow: hidden;
padding: 0 30px;
position: relative;
width: 100%;
}
.page-head__logo {
background-image: none;
float: left;
padding: 0;
text-shadow: none;
width: 200px;
}
/* Nav */
.site-nav {
display: block;
float: right;
text-align: center;
width: auto;
}
.site-nav__list {
list-style: none;
margin: 0;
padding: 0;
position: relative;
top: auto;
left: auto;
width: auto;
}
.site-nav__list li {
background: none;
display: block;
float: left;
margin: 0;
padding-left: 0;
text-transform: uppercase;
}
.site-nav__list a {
box-sizing: border-box;
display: block;
font-weight: 900;
padding: 30px 15px;
transition: color .15s;
text-shadow: none;
}
.site-nav__list a {
color: red;
}
/* Underline */
.site-nav__line {
background: red;
content: "";
display: block;
height: 2px;
position: absolute;
top: -2px;
left: 0;
width: 100%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body>
<header class="page-head">
Logo Here
<nav class="site-nav ">
<ul class="site-nav__list">
<li class="site-nav__item ">About</li>
<li class="site-nav__item">Looooonger Title</li>
<li class="site-nav__item">Company</li>
<li class="site-nav__item">About</li>
<li class="site-nav__item">Login</li>
<li class="site-nav__item">Apply</li>
</ul>
</nav>
</header>
</body>
if your problem is you to double click it before redirecting to its page, Try thi
$('.site-nav__list a').click(function(){
$(this).click();
});
the function is when you click the navigation the script will click it again,
If you're sure that the cause of the problem is running that script on mobile screens, you can call sliding script only on desktops with this code:
if ( $(window).width() > 739) {
//Desktop scripts
}
else {
//mobile scripts
}
You can change the screen width of devices you want to script work on them by changing 739. After that your script will run only on screens larger that 739px or what you've choose.
Update
If you want to everything works correct after resizing, You should do a little trick.
Personally, I use this method because it's the only way that makes you sure about bugs and problems. The trick is reloading the page after resizing.
It's not costly in many cases because most of the things cashed and don't need to redownloading. There are lots of methods to do that, but I use the below one because it works good and is simple and short:
window.onresize = function () {
location = location;
}
You just need to add this lines at the end of your script file. After resizing, everything will work well again.
How it works?
When you resize the window, a javascript event will emit. What we done in the last code is overriding the event listener of that event. So when the user resize the window, the location = location; code will execute.
What this line means? the location object is a property of window object and keeping information about current window url. When you change the location of a window, browser page will reload to getting the new window of the new location (more info about location).
What we done here is assigning current location to the location. So browser thinks we had a redirect request and reloads the page. But because the new location is the same object as previous one, the page will reload instead of redirecting to somewhere else.

Forcing mobile devices to activate :hover CSS properties on first touch and activate link on second touch

: )
So, I'm trying to solve a hover effect issue. I have tooltips on some of my links. Code looks like this:
<a href="https://wikipedia.org/wiki/Space_Shuttle_Atlantis">
<h6 class="has-tip">Space Shuttle
<p class="tip">The space shuttle was invented by Santa Claus</p>
</h6>
</a>
And the CSS is a bit more involved:
.tip {
display: block;
position: absolute;
bottom: 100%;
left: 0;
width: 100%;
pointer-events: none;
padding: 20px;
margin-bottom: 15px;
color: #fff;
opacity: 0;
background: rgba(255,255,255,.8);
color: coal;
font-family: 'Ubuntu Light';
font-size: 1em;
font-weight: normal;
text-align: left;
text-shadow: none;
border-radius: .2em;
transform: translateY(10px);
transition: all .25s ease-out;
box-shadow: 2px 2px 6px rgba(0, 0, 0, 0.28);
}
.tip::before {
content: " ";
display: block;
position: absolute;
bottom: -20px;
left: 0;
height: 20px;
width: 100%;
}
.tip::after { /* the lil triangle */
content: " ";
position: absolute;
bottom: -10px;
left: 50%;
height: 0;
width: 0;
margin-left: -13px;
border-left: solid transparent 10px;
border-right: solid transparent 10px;
border-top: solid rgba(255,255,255,.8) 10px;
}
.has-tip:hover .tip {
opacity 1;
pointer-events auto;
transform translateY(0px);
}
Now, on desktop this works wonderfully. You hover over the tiny title and you get a pretty looking tooltip, then if you click anywhere on the title or tooltip (unless you decide to put yet another link in the paragraph which works separately and nicely) you activate the link. Yay : )
Now on mobile, the whole thing gets funky. Touching just activates the link. If you have slow internet, or iOS, you might glimpse the tooltip just as the next page loads.
I would like the following behavior:
User taps on tiny title (h6) which has class (has-tip)
If this is the first tap, the tooltip shows, and nothing else happens. 3)
If the tooltip is already showing when they tap (as in a subsequent
tap) then the link is activate and the new page loads.
Any ideas how I might implement this? No jQuery if possible.
One way to do it is to save a reference to the last clicked has-tip link and to apply a class to it which forces the tip to show. When you click on a link and it matches the the last one clicked, you let the event pass.
EDIT: oh, I forgot to mention you might need a classList shim for old IE.
JSFiddle link.
HTML
<a href="http://jsfiddle.net/1tc52muq/5/" class="has-tip">
JSFiddle<span class="tip">Click for some fun recursion</span>
</a><br />
<a href="http://google.com" class="has-tip">
Google<span class="tip">Click to look for answers</span>
</a>
JS
lastTip = null;
if(mobile) {
var withtip = document.querySelectorAll(".has-tip");
for(var i=0; i<withtip.length; ++i) {
withtip[i].addEventListener("click", function(e) {
if(lastTip != e.target) {
e.preventDefault();
if(lastTip) lastTip.classList.remove("force-tip");
lastTip = e.target;
lastTip.classList.add("force-tip");
}
});
}
}
CSS
.has-tip {
position: abolute;
}
.tip {
display: none;
position: relative;
left: 20px;
background: black;
color: white;
}
.has-tip:hover .tip, .force-tip .tip {
display: inline-block;
}
Edit: Just wanted to say that Jacques' approach is similar, but much more elegant.
On touch devices, you'll need to make a click/tap counter:
1) On first tap of any link, store the link and display the hover state.
2) On another tap, check to see if it's the same as the first, and then perform the normal tap action if it is. Otherwise, clear any existing hovers, and set the new tap target as the one to count.
3) Reset / clear any hovers if you tap on non-links.
I've made a rudimentary JSFiddle that console.logs these actions. Since we're not using jQuery, I didn't bother with adding/removing CSS classes on the elements.
Also, sorry about not writing taps instead of clicks.
var clickTarget;
var touchDevice = true;
if(touchDevice) {
var links = document.getElementsByTagName('a');
for(var i=0; i<links.length; i++) {
links[i].onclick = function(event) {
event.stopPropagation();
event.preventDefault(); // this is key to ignore the first tap
checkClick(event);
};
};
document.onclick = function() {
clearClicks();
};
}
var checkClick = function(event) {
if(clickTarget === event.target) {
// since we're prevent default, we need to manually trigger an action here.
console.log("Show click state and also perform normal click action.");
clearClicks();
} else {
console.log("New link clicked / Show hover");
clickTarget = event.target;
}
}
var clearClicks = function() {
console.log("Clearing clicks");
clickTarget = undefined;
};
http://jsfiddle.net/doydLt6v/1/

Categories

Resources