Animating scrollbar width using setTimeout function and inserting rules for pseudo selector - javascript

I am trying to increase scrollbar width slowly when hovering on it. Using javascript by adding a class with the -webkit-scrollbar pseudo selector I was able to expand the scrollbar on hover. But I was instant, not smooth. I want a transition width 2s like effect. But adding transition animation using css didn't work. Some comments said that it's not possible by css. I am trying to achieve this using setTimeout function. Also applying new rules to the pseudo selector. I had to remove rules to make this work as the browser isn't quite allowing direct scrollbar width change. But the problem with my code is, only for the first time I hover on scrollbar - it expands showing the intended change/animation. But after that it just expands instantly on hover without the setTimeout animation effect.
CSS
.articles-container::-webkit-scrollbar
{
width: 20px;
}
.articles-container::-webkit-scrollbar-thumb
{
border-radius: 40px;
background-color: rgba(112,112,112,0.3);
}
.articles-container::-webkit-scrollbar-thumb:hover
{
background-color: rgba(112,112,112,0.5);
}
JAVASCRIPT
setTimeout(function()
{
var myDiv = document.querySelector('.articles-container');
myDiv.addEventListener('mouseover', function(event)
{
// Get the mouse coordinates
const mouseX = event.clientX;
const mouseY = event.clientY;
// Get the position and dimensions of the scrollbar
const scrollbar = myDiv.getBoundingClientRect();
const scrollbarWidth = myDiv.offsetWidth - myDiv.clientWidth;
// console.log(scrollbarWidth);
const scrollbarX = scrollbar.right - scrollbarWidth;
const scrollbarY = scrollbar.top;
// const scrollbarWidth = 16;
var scrollbarHeight = scrollbar.height;
// console.log(scrollbarHeight);
function scrollbarAni()
{
const style = document.getElementsByTagName('style')[0];
setTimeout(function(){
// style.sheet.removeRule('.articles-container.expanded::-webkit-scrollbar { width: 20px; }', 0);
style.sheet.insertRule('.articles-container.expanded::-webkit-scrollbar { width: 30px; }', 0);
}, 1000);
setTimeout(function(){
style.sheet.removeRule('.articles-container.expanded::-webkit-scrollbar { width: 30px; }', 0);
style.sheet.insertRule('.articles-container.expanded::-webkit-scrollbar { width: 33px; }', 0);
}, 1100);
setTimeout(function(){
style.sheet.removeRule('.articles-container.expanded::-webkit-scrollbar { width: 33px; }', 0);
style.sheet.insertRule('.articles-container.expanded::-webkit-scrollbar { width: 36px; }', 0);
}, 1200);
setTimeout(function(){
style.sheet.removeRule('.articles-container.expanded::-webkit-scrollbar { width: 36px; }', 0);
style.sheet.insertRule('.articles-container.expanded::-webkit-scrollbar { width: 39px; }', 0);
}, 1300);
setTimeout(function(){
style.sheet.removeRule('.articles-container.expanded::-webkit-scrollbar { width: 39px; }', 0);
style.sheet.insertRule('.articles-container.expanded::-webkit-scrollbar { width: 42px; }', 0);
}, 1400);
setTimeout(function(){
style.sheet.removeRule('.articles-container.expanded::-webkit-scrollbar { width: 42px; }', 0);
style.sheet.insertRule('.articles-container.expanded::-webkit-scrollbar { width: 45px; }', 0);
}, 1500);
setTimeout(function(){
style.sheet.removeRule('.articles-container.expanded::-webkit-scrollbar { width: 45px; }', 0);
style.sheet.insertRule('.articles-container.expanded::-webkit-scrollbar { width: 48px; }', 0);
}, 1600);
setTimeout(function(){
style.sheet.removeRule('.articles-container.expanded::-webkit-scrollbar { width: 48px; }', 0);
style.sheet.insertRule('.articles-container.expanded::-webkit-scrollbar { width: 51px; }', 0);
}, 1700);
setTimeout(function(){
style.sheet.removeRule('.articles-container.expanded::-webkit-scrollbar { width: 51px; }', 0);
style.sheet.insertRule('.articles-container.expanded::-webkit-scrollbar { width: 54px; }', 0);
}, 1800);
setTimeout(function(){
style.sheet.removeRule('.articles-container.expanded::-webkit-scrollbar { width: 54px; }', 0);
style.sheet.insertRule('.articles-container.expanded::-webkit-scrollbar { width: 57px; }', 0);
}, 1900);
setTimeout(function(){
style.sheet.removeRule('.articles-container.expanded::-webkit-scrollbar { width: 57px; }', 0);
style.sheet.insertRule('.articles-container.expanded::-webkit-scrollbar { width: 60px; }', 0);
}, 2000);
}
// Check if the mouse is on the scrollbar
if (mouseX >= scrollbarX && mouseX < scrollbarX + scrollbarWidth &&
mouseY >= scrollbarY && mouseY < scrollbarY + scrollbarHeight)
{
console.log('Mouse is on the scrollbar');
myDiv.classList.add('expanded');
scrollbarAni();
}
else
{
console.log('Mouse is not on the scrollbar');
myDiv.classList.remove('expanded');
}
});
myDiv.addEventListener('mouseout', function(event) { myDiv.classList.remove('expanded'); });
}, 1000);
How can I make this work like every time hovering on scrollbar the scrollbarAni() function will be called. Right now it's working only for the first hover. Please any workaround suggestion will be really helpful.

Related

The equivalent of pointer-events:none in javascript

I'm facing a problem with wiggling bottom border pseudo css element, pointer events set to none fixes it but I have decide to go with javascript and create a span on mouse enter event and remove it on mouse leave event. with set time method, I created an acceptable animation like feeling but now back to the same problem WIGGLING. So is there an equivalent code in javascript that mimics pointer events set to none?
<a class="top-link">
<span class="title"> Title </span>
</a>
.border-bottom {
border-bottom: solid 3px #ddd;
content: "";
width: 100%;
position: absolute;
top: 100%;
z-index: 1;
left: 0;
transform: translateY(4px);
transition: ease-in 0.2s;
opacity: 0;
}
document.querySelectorAll(".top-link").forEach((el) => {
el.addEventListener("mouseenter", function () {
el.insertAdjacentHTML("beforeend", '<span class="border-bottom"></span>');
el.childNodes.forEach((e) => {
if (e.className == "border-bottom") {
setTimeout(() => {
e.style.transform = "translateY(0px)";
e.style.opacity = "1";
}, 50);
}
});
});
el.addEventListener("mouseleave", function () {
el.childNodes.forEach((e) => {
if (e.className == "border-bottom") {
setTimeout(() => {
e.style.transform = "translateY(4px)";
}, 25);
setTimeout(() => {
e.style.opacity = "0.4";
}, 50);
setTimeout(() => {
e.style.opacity = "0";
e.remove();
}, 250);
}
});
});
});

Add element using javascript at specific position

I'm creating a small game to use in one of my English classes. I've managed to add all the items and create the animations of the items.
What I need now is to place the items in their fixed and starting positions. The blue and red items have their fixed positions at the bottom corner of each side and the soccerball starts in the middle before any movement.
I don't know how to do this. Can anybody help me or point me in the right direction, please.
This is what I have now:
This is what I would like to do:
This is my code so far
<script>
//Set Background
document.body.style.backgroundImage =
"url('./back.jpg')";
document.body.style.backgroundSize = 'contain';
document.body.style.backgroundRepeat = 'no-repeat';
document.body.style.backgroundPosition = 'center';
document.body.style.backgroundSize = '100%';
//Add images
function show_image(src, width, height, alt, id) {
var img = document.createElement("img");
img.src = src;
img.width = width;
img.height = height;
img.alt = alt;
img.id = id;
if (id == "s") {
img.style.position = "center"
}
document.body.appendChild(img);
}
show_image("./b.png", 100, 100, "btnBlue", "b")
show_image("./r.png", 100, 100, "btnRed", "r")
show_image("./s.png", 100, 100, "btnSoccerBall", "s")
//Add onclick
document.getElementById("b").addEventListener("click", myMoveLeft);
document.getElementById("r").addEventListener("click", myMoveRight);
//Add animation
var item = document.getElementById('s');
var anim;
var x = 0, y = 0;
function myMoveLeft() {
anim = item.animate([
// keyframes
{ transform: `translate(${x}px, ${y}px)` },
{ transform: `translate(${x - 60}px, ${y}px)` }
], {
duration: 1000,
iterations: 1,
fill: 'forwards'
});
x -= 60;
}
function myMoveRight() {
anim = item.animate([
// keyframes
{ transform: `translate(${x}px, ${y}px)` },
{ transform: `translate(${x + 60}px, ${y}px)` }
], {
duration: 1000,
iterations: 1,
fill: 'forwards'
});
x += 60;
}
item.addEventListener('animationend', () => {
console.log('Animation ended');
});
</script>
I think what you want to achieve can be done with CSS, specifically by using Flexbox.
body {
font-family: Arial, Helvetica, sans-serif;
font-size: 5rem;
}
.btnBlue {
color: blue;
}
.btnRed {
color: red;
}
.btnSoccerBall {
color: white;
position: relative;
margin: auto;
}
.soccer-field {
background-color: lightgreen;
height: 100vh;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: flex-end;
padding: 0 20px; /*optional*/
}
<html lang="en">
<body>
<div class="soccer-field">
<div class="btnBlue">O</div>
<div class="btnSoccerBall">O</div>
<div class="btnRed">O</div>
</div>
</body>
</html>

Assist me with understanding the reason of slow script performance

I repeat usual script for placeholder (jPlaceholder.js).
The reason is slow reading speed(~150 000\1000ms) and big file size (5.8kb);
The script below has a much faster reading speed (~1 950 000\1000ms) and is more lightweight(1.8kb).
The problem is that the first one runs ~32 000\1000ms, but my(script below) 525\1000ms only.
Please help me to understand why, and how to make it faster.
$.fn.placehold = function (option) {
var opt = {
wrapperClass: 'jvPlacehold',
wrapperPadding: 0,
wrapperMargin: 0,
wrapperTop: 0,
wrapperLeft: 0,
counterClass: 'jvPhCounter',
labelClass: 'jvPhLabel',
labelPadding: '0 5px',
labelColor: '#a9a9a9',
labelOpacitySpeed: 300,
holderDataName: 'placeholder',
focusSpeed: 300,
blurSpeed: 300
};
opt = $.extend({}, opt, option);
var $el = $(this),
$wrapper = $('<div>', {
class: opt.wrapperClass,
group: 'search',
css: {
overflow: 'hidden',
background: $el.css('background'),
width: $el.outerWidth(),
height: $el.outerHeight(),
padding: opt.wrapperPadding,
margin: opt.wrapperMargin,
top: opt.wrapperTop,
left: opt.wrapperLeft,
float: opt.wrapperFloat
}
}),
$counter = $('<span>', {
class: opt.counterClass,
css: {
display: 'none'
}
}),
$label = $('<span>', {
class: opt.labelClass,
text: $el.data(opt.holderDataName),
css: {
padding: opt.labelPadding,
color: opt.labelColor
}
});
$el.css({
position: 'absolute',
background: 'none',
left: 0
});
$el.after($wrapper.append($counter, $label)).appendTo($wrapper);
$label.css('top', Math.abs($el.innerHeight() - $label.height()) / 2);
$el.keyup(function () {
if ($counter.text($el.val().replace(/\s/gi, '❙')).outerWidth() >= parseInt($label.css('left'))) $label.stop().animate({
opacity: 0
}, opt.labelOpacitySpeeed);
else $label.stop().animate({
opacity: 1
}, opt.labelOpacitySpeeed);
}).focus(function () {
$label.stop().animate({
left: ($el.innerWidth() - $label.outerWidth())
}, opt.focusSpeed);
}).blur(function () {
if (!$el.val().length) $label.stop().animate({
left: 0
}, opt.blurSpeed);
});
};
$('#search').placehold();
div, span {
position:relative;
}
input {
padding:2px 5px;
border:1px solid #a9a9a9;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
<input type="text" id="search" data-placeholder="Search">
var tmp = new Date().getTime();
Write above, then use:
console.log(new Date().getTime() - tmp);
between codes to see how much time each part of code takes.

Empty space appearing at the end of my carousel

I have used this script to create an infinite carousel on my website here. It's been customized with CSS so the first and last items are displayed half way.
If you keep clicking the right arrow, you will end up hitting an empty space at the end. So far I haven't been able to fix this. Can anybody offer any solutions?
Here is the relevant script:
/**
* #author Stéphane Roucheray
* #extends jquery
*/
jQuery.fn.simplecarousel = function(previous, next, options){
var sliderList = jQuery(this).children()[0];
if (sliderList) {
var increment = jQuery(sliderList).children().outerWidth(true),
elmnts = jQuery(sliderList).children(),
numElmts = elmnts.length,
sizeFirstElmnt = increment,
shownInViewport = Math.round(jQuery(this).width() / sizeFirstElmnt),
firstElementOnViewPort = 1,
isAnimating = false;
for (i = 0; i < shownInViewport; i++) {
jQuery(sliderList).css('width',(numElmts+shownInViewport)*increment + increment + "px");
jQuery(sliderList).append(jQuery(elmnts[i]).clone());
}
jQuery(previous).click(function(event){
if (!isAnimating) {
if (firstElementOnViewPort == 1) {
jQuery(sliderList).css('left', "-" + numElmts * sizeFirstElmnt + "px");
firstElementOnViewPort = numElmts;
}
else {
firstElementOnViewPort--;
}
jQuery(sliderList).animate({
left: "+=" + increment,
y: 0,
queue: true
}, "swing", function(){isAnimating = false;});
isAnimating = true;
}
});
jQuery(next).click(function(event){
if (!isAnimating) {
if (firstElementOnViewPort > numElmts) {
firstElementOnViewPort = 2;
jQuery(sliderList).css('left', "0px");
}
else {
firstElementOnViewPort++;
}
jQuery(sliderList).animate({
left: "-=" + increment,
y: 0,
queue: true
}, "swing", function(){isAnimating = false;});
isAnimating = true;
}
});
}
};
#home-carousel-container {
position: relative;
}
#home-carousel {
overflow: hidden;
}
#home-carousel ul {
margin-left: -143px;
padding: 0;
position: relative;
}
#home-carousel li {
float: left;
height: 645px;
list-style: none outside none;
margin: 0 3px;
width: 256px;
}
As per my comment.
You have set a negative left-margin on your carousel causing it to hide half of an image. As a result when you click next/previous, it shows where an image is moved to create the continuous affect.
Witihin your css, I changed
#home-carousel ul{
position: relative;
padding: 0;
margin-left: -143px;
}
to
#home-carousel ul{
position: relative;
padding: 0;
margin-left: -3px;
}
And had no problems what so ever.

JavaScript Modal is Too Big for Screen

I have created a modal window using the Prototype JavaScript framework and the following object:
var Modal = Class.create({
animate: function () {
this.step = (this.step + 1) % 31;
if (this.throbber) {
this.throbber.setStyle({
backgroundPosition: 'center ' + this.step * -33 + 'px'
});
}
},
destroy: function () {
if (this.interval_id) {
window.clearInterval(this.interval_id);
}
if (this.timeout_id) {
window.clearTimeout(this.timeout_id);
}
if (this.overlay.parentNode) {
this.overlay.remove();
}
if(this.window.select('select')){
this.window.select('select').each(function(ele){
Element.remove(ele);
});
}
this.window.select('*').invoke('remove');
if (this.window.parentNode) {
this.window.remove();
}
},
initialize: function (element) {
this.launch_element = element;
this.overlay = new Element('div', {'class': 'modal_overlay'});
$$('body').first().insert(this.overlay);
this.close = new Element('a', {
'class': 'modal_close'
}).insert('Close');
this.close.observe('click', this.destroy.bind(this));
this.window = new Element('div', {'class': 'modal_window'});
if(this.window.select('select')){
this.window.select('select').each(function(ele){
Element.remove(ele);
});
}
this.window.select('*').invoke('remove');
this.window.insert(this.close);
this.section = new Element('div', {'class': 'section'});
this.show_throbber();
this.window.insert(this.section);
$$('body').first().observe('keypress', function (e) {
var key_code = window.event ? event.keyCode : e.keyCode;
var esc = window.event ? 27 : e.DOM_VK_ESCAPE;
if (key_code === esc) {
this.destroy();
}
}.bind(this));
$$('.container').first().insert(this.window);
if (Prototype.Browser.IE) {
this.scroll_window();
Event.observe(window, 'scroll', this.scroll_window.bind(this));
}
},
remove_throbber: function () {
if (this.interval_id) {
window.clearInterval(this.interval_id);
}
if (this.timeout_id) {
window.clearTimeout(this.timeout_id);
}
this.throbber.remove();
},
scroll_window: function() {
var height, offsets;
offsets = document.viewport.getScrollOffsets();
this.overlay.setStyle({
left: offsets.left + 'px',
top: offsets.top + 'px'
});
height = document.viewport.getHeight();
this.window.setStyle({
top: offsets.top + Math.round(17 * height / 100) + 'px'
});
},
show_throbber: function (text) {
if(this.section.select('select')){
this.section.select('select').each(function(ele){
Element.remove(ele);
});
}
this.section.select('*').invoke('remove');
if (!text) {
text = 'Loading';
}
this.throbber = new Element('div', {
'class' : 'modal_throbber'
}).insert(new Element('p').insert(text + '...'));
this.section.insert(this.throbber);
this.step = 0;
this.interval_id = window.setInterval(this.animate.bind(this), 50);
this.complete = false;
this.timeout_id = window.setTimeout(this.timeout.bind(this), 20000);
},
timeout: function () {
var div, p, span;
if (!this.complete) {
if (this.interval_id) {
window.clearInterval(this.interval_id);
}
if (this.throbber) {
this.remove_throbber();
span = new Element('span', {'class': 'icon icon-delete'});
p = new Element('p', {'class': 'first'}).update(span);
p.insert('There seems to be something wrong with eSP. ' +
'Please try again later.');
div = new Element('div', {'class': 'error'}).update(p);
this.section.update(div);
}
if (this.timeout_id) {
window.clearTimeout(this.timeout_id);
}
}
}
});
and is styled with the following stylesheet:
.modal_overlay {
height: 100%;
width: 100%;
position: fixed;
left: 0;
top: 0;
z-index: 2999;
opacity: 0.5;
background: #000;
}
* html .modal_overlay {
filter: alpha(opacity=50);
position: absolute;
zoom: 1;
}
.modal_window {
display: block;
position: fixed;
top: 17%;
z-index: 3000;
}
* html .modal_window {
position: absolute;
}
.modal_close {
background: url('/images/close.gif') no-repeat scroll 0 0;
height: 26px;
cursor: pointer;
position: absolute;
right: -13px;
top: -8px;
width: 26px;
text-indent: -10000px;
}
.modal_throbber {
background: url('/images/throbber.png') no-repeat top center;
padding-top: 32px;
}
.modal_throbber p {
background: #fff;
text-align: center;
}
We are now getting reports from customers that when the modal window is taller than the screen they cannot scroll down to see the bottom part of the modal window's content.
Is it a case of us making sure that too much information isn't displayed in the modal or is there a better, more technical fix?
You can put a max-height on the modal window and overflow:scroll.

Categories