How to use scrollIntoView on container which has overflow:hidden and it shouldn't scroll the page?
Here is an example: at the bottom of the page text in container <div class="cnt'> which has fixed width and overflow hidden. I want to scroll items in this container without scrolling the page.
At the top of the page two buttons to scroll to first and last element. If i click on button it will scroll text in the container and scroll to that container at the bottom of the page.
I can't use scrollLeft because overflow is hidden. :(
Does anybody know how to solve it?
const cnt = document.querySelector('.cnt')
const spanElements = cnt.querySelectorAll('span');
const lastSpan = spanElements[spanElements.length - 1]
const firstSpan = spanElements[0]
lastSpan.scrollIntoView()
const buttons = Array.from(document.querySelectorAll('button'))
const [buttonToFirstEl, buttonToLastEl] = buttons;
buttonToFirstEl.onclick = function() {
firstSpan.scrollIntoView()
}
buttonToLastEl.onclick = function() {
lastSpan.scrollIntoView()
}
.cnt {
width: 90px;
overflow: hidden;
white-space: nowrap;
padding: 8px;
border: solid #ccc 1px;
}
.filler {
width: 100%;
height: 200px;
border: dashed 2px #ccc;
margin: 20px;
}
.root {
border: solid 1px;
}
<div class="root">
<button id="button">scroll to first element</button>
<button id="button">scroll to last element</button>
<div class="filler">
</div>
<div class="filler">
</div>
<div class="filler">
</div>
<div class="filler">
</div>
<div class="cnt">
<span>
first:tessst
</span>
<span>
2:dddd
</span>
<span>
3:cccddd
</span>
<span>
4:rreeee
</span>
<span>
last:dddrreddd
</span>
</div>
</div>
https://codepen.io/geeny273/pen/bGpxYqG
if you want scrolled element in .cnt class
you need to style every span in .cnt
.cnt span{
/* relative + block for make elements solid & listed*/
position : relative;
display : block;
color: red;
font-size : 16px;
}
and in .cnt parent you need to defined height for making scroll working
.cnt {
width: 90px;
/* for example 28px */
height : 28px;
overflow: hidden;
white-space: nowrap;
padding: 4px;
border: solid black 1px;
/* overflow y or x for making scroll*/
overflow-y : scroll;
}
i hope this steps help you
To get this correctly, (i.e with support for all writing-mode, direction and block-inline options) you'd basically have to rewrite scrollIntoView from scratch, while omitting the loop that goes up the tree of scrolling-boxes. This is not a trivial task.
If you don't need a bullet proof solution though, you can simply get the nearest scrolling-box and check by how much it should be scrolled to show the start of your element:
const cnt = document.querySelector('.cnt')
const spanElements = cnt.querySelectorAll('span');
const lastSpan = spanElements[spanElements.length - 1]
const firstSpan = spanElements[0]
const buttons = Array.from(document.querySelectorAll('button'))
const [buttonToFirstEl, buttonToLastEl] = buttons;
buttonToFirstEl.onclick = function() {
scrollIntoParentView( firstSpan );
}
buttonToLastEl.onclick = function() {
scrollIntoParentView( lastSpan );
}
function scrollIntoParentView( elem, options ) {
const directions = getSimpleScrollIntoViewDirections( elem, options );
const left = directions.inline_direction || 0;
const top = directions.block_direction || 0;
const new_options = {
left,
top,
behavior: (options && options.behavior) || "auto"
};
directions.scrolling_box.scrollBy( new_options );
}
function getSimpleScrollIntoViewDirections( elem, { block = "start", inline = "start" } = {} ) {
const element_bbox = elem.getBoundingClientRect();
const scrolling_box = getNearestScrollingBox( elem );
const scrolling_box_bbox = scrolling_box.getBoundingClientRect();
const block_direction = Math.round( element_bbox.top - scrolling_box_bbox.top);
const inline_direction = Math.round( element_bbox.left - scrolling_box_bbox.left);
return {
scrolling_box,
block_direction,
inline_direction
};
}
function getNearestScrollingBox( elem ) {
if( !elem.isConnected ) { return null; }
const elem_computed_styles = getComputedStyle( elem );
// not in specs, but that seems to be what browser implementations do
if( elem_computed_styles.getPropertyValue( 'position' ) === "fixed" ) {
return null;
}
const scrolling_box = elem.parentElement;
if( !scrolling_box ) {
return elem === document.scrollingElement ? null : document.scrollingElement;
}
const computed_styles = getComputedStyle( scrolling_box );
const scroll_x = computed_styles.getPropertyValue( "overflow-x");
const scroll_y = computed_styles.getPropertyValue( "overflow-y");
if(
(scroll_x === 'clip' && scroll_y === 'clip') ||
(scrolling_box.offsetHeight <= scrolling_box.scrollingHeight) ||
(scrolling_box.offsetWidth <= scrolling_box.scrollingWidth)
) {
return getNearestScrollingBox( scrolling_box );
}
return scrolling_box;
}
.cnt {
width: 90px;
overflow: hidden;
white-space: nowrap;
padding: 8px;
border: solid #ccc 1px;
}
.filler {
width: 100%;
height: 200px;
border: dashed 2px #ccc;
margin: 20px;
}
.root {
border: solid 1px;
}
<div class="root">
<button id="button">scroll to first element</button>
<button id="button">scroll to last element</button>
<div class="filler">
</div>
<div class="filler">
</div>
<div class="filler">
</div>
<div class="filler">
</div>
<div class="cnt">
<span>
first:tessst
</span>
<span>
2:dddd
</span>
<span>
3:cccddd
</span>
<span>
4:rreeee
</span>
<span>
last:dddrreddd
</span>
</div>
</div>
scrollLeft and scrollTop work well with overflow:hidden. I check scrollLeft property on first span element instead of its container
changing scrollLeft on parent element of span, solve this issue
firstSpan.parentElement.scrollLeft = 0;
const cnt = document.querySelector('.cnt')
const spanElements = cnt.querySelectorAll('span');
const lastSpan = spanElements[spanElements.length - 1]
const firstSpan = spanElements[0]
lastSpan.scrollIntoView()
const buttons = Array.from(document.querySelectorAll('button'))
const [buttonToFirstEl, buttonToLastEl] = buttons;
buttonToFirstEl.onclick = function() {
firstSpan.parentElement.scrollLeft = 0;
}
buttonToLastEl.onclick = function() {
const cnt = lastSpan.parentElement;
cnt.scrollLeft = cnt.scrollWidth;
}
.cnt {
width: 90px;
overflow: hidden;
white-space: nowrap;
padding: 8px;
border: solid #ccc 1px;
}
.filler {
width: 100%;
height: 200px;
border: dashed 2px #ccc;
margin: 20px;
}
.root {
border: solid 1px;
}
<div class="root">
<button id="button">scroll to first element</button>
<button id="button">scroll to last element</button>
<div class="filler">
</div>
<div class="filler">
</div>
<div class="filler">
</div>
<div class="filler">
</div>
<div class="cnt">
<span>
first:tessst
</span>
<span>
2:dddd
</span>
<span>
3:cccddd
</span>
<span>
4:rreeee
</span>
<span>
last:dddrreddd
</span>
</div>
</div>
Related
I can't figure out how to close one submenu when another one is open. I'm not sure if html is needed here, so I'm just attaching JS code here:
const burgerBtn = document.querySelector(".header__burger"),
menu = document.querySelector(".menu"),
body = document.querySelector(".body"),
filter = document.querySelector(".filter"),
blockFilter = document.querySelectorAll(".block-filter"),
dropdown = document.querySelectorAll(".block-filter__dropdown");
if (filter) {
blockFilter.forEach(item => {
item.addEventListener("click", event => {
item.querySelector(".block-filter__dropdown").classList.toggle("block-filter__dropdown_state_active");
item.querySelector(".block-filter__icon").classList.toggle("block-filter__icon_state_active");
if (event.target.classList.contains("block-filter__item")) {
item.querySelector(".block-filter__value").textContent = event.target.textContent;
}
})
})
}
<div class="filter hero__filter">
<form class="filter__form">
<div class="filter__block block-filter">
<div class="block-filter__button">
<div class="block-filter__header">
<span class="block-filter__type">Purpose</span>
<div class="block-filter__icon"></div>
</div>
<span class="block-filter__value">Buy</span>
</div>
<div class="block-filter__dropdown">
<span class="block-filter__item">Buy</span>
<span class="block-filter__item">Sell</span>
</div>
</div>
Sure, just remove the class from the active one first:
item.addEventListener("click", (event) => {
// get active, and if it exists, remove active
document.querySelector(".block-filter__dropdown_state_active")?.classList.remove("block-filter__dropdown_state_active");
item.querySelector(".block-filter__dropdown").classList.toggle(
"block-filter__dropdown_state_active"
);
item.querySelector(".block-filter__icon").classList.toggle(
"block-filter__icon_state_active"
);
if (event.target.classList.contains("block-filter__item")) {
item.querySelector(".block-filter__value").textContent =
event.target.textContent;
}
});
We use ?. here to prevent us from going further (and causing an error) if there is no active dropdown already.
What you need to do is look for a currently active item first and "de-activate" them. You should also check that the currently active item is not the clicked item as you already have logic defined for that.
I've expanded on your snippet to create a solution.
NOTE: It might be useful creating a separate function/s for handling to "activate" and "de-activate" code where you pass in a .block-filter element.
const burgerBtn = document.querySelector(".header__burger"),
menu = document.querySelector(".menu"),
body = document.querySelector(".body"),
filter = document.querySelector(".filter"),
blockFilter = document.querySelectorAll(".block-filter"),
dropdown = document.querySelectorAll(".block-filter__dropdown");
if (filter) {
blockFilter.forEach(item => {
item.addEventListener("click", event => {
const active_dropdown = document.querySelector(".block-filter__dropdown_state_active");
if(active_dropdown !== null){
// get parent until we find ".block-filter"
const active_item = active_dropdown.closest(".block-filter");
// check it's not the current item
if(active_item !== null && active_item !== item){
// apply same logic as below to remove active state
active_item.querySelector(".block-filter__dropdown").classList.remove("block-filter__dropdown_state_active");
active_item.querySelector(".block-filter__icon").classList.remove("block-filter__icon_state_active");
}
}
// your original logic
item.querySelector(".block-filter__dropdown").classList.toggle("block-filter__dropdown_state_active");
item.querySelector(".block-filter__icon").classList.toggle("block-filter__icon_state_active");
if (event.target.classList.contains("block-filter__item")) {
item.querySelector(".block-filter__value").textContent = event.target.textContent;
}
})
})
}
/* base styles */
* {
box-sizing: border-box;
}
html {
font-family: sans-serif;
background-color: #f3f3f3;
}
.filter.hero__filter {
width:600px;
margin:auto;
border: 2px solid #eee;
background-color: #fff;
}
.filter__form {
display:flex;
}
.filter__block {
flex: 1;
padding: 5px;
position: relative;
}
.block-filter__header {
font-weight:600;
font-size:12px;
color: #555;
}
.block-filter__dropdown {
display:none;
position:absolute;
top:100%;
left:0;
right:0;
background-color:#fff;
border: 1px solid #ccc;
box-shadow: 0 2px 4px rgb(0 0 0 / 10%);
border-radius:4px;
}
.block-filter__dropdown_state_active {
display: block;
}
.block-filter__item {
padding: 5px 10px;
display:block;
border-bottom: 1px solid #eee;
}
.block-filter__item:last-child {
border-bottom: none;
}
<div class="filter hero__filter">
<form class="filter__form">
<div class="filter__block block-filter">
<div class="block-filter__button">
<div class="block-filter__header">
<span class="block-filter__type">Purpose</span>
<div class="block-filter__icon"></div>
</div>
<span class="block-filter__value">Buy</span>
</div>
<div class="block-filter__dropdown">
<span class="block-filter__item">Buy</span>
<span class="block-filter__item">Sell</span>
</div>
</div>
<div class="filter__block block-filter">
<div class="block-filter__button">
<div class="block-filter__header">
<span class="block-filter__type">Second</span>
<div class="block-filter__icon"></div>
</div>
<span class="block-filter__value">Alpha</span>
</div>
<div class="block-filter__dropdown">
<span class="block-filter__item">Bravo</span>
<span class="block-filter__item">Charlie</span>
<span class="block-filter__item">Delta</span>
</div>
</div>
</form>
</div>
I have this HTML code in which I want to display two images in parallel. It should work like a change slider image with scroll option:
The problem is that I can't scroll down into the div with class .image-comparison-slider.
#image-comparison-slider {
margin: auto;
position: relative;
width: var(--image-comparison-slider-width);
overflow: auto;
border-radius: 0.5rem;
box-shadow: -7px 5px 16px 1px rgba(56, 86, 122, 0.6);
cursor: col-resize;
}
#image-comparison-slider img {
display: block;
width: var(--image-comparison-slider-width);
height: auto;
max-height: 80vh;
object-fit: cover;
pointer-events: none;
user-select: none;
}
<div id="image-comparison-slider">
<img class="overflow-auto" width="100%" src="../../static/img/screenshot.png" alt="before">
<div class="img-wrapper">
<img width="100%" alt="after" src="../../static/img/screenshot1.png" />
</div>
<span class="label label-before">Before</span>
<span class="label label-after">After</span>
<div class="handle">
<div class="handle-line"></div>
<div class="handle-circle">
<i class="fas fa-chevron-left"></i>
<i class="fas fa-chevron-right"></i>
</div>
<div class="handle-line"></div>
</div>
</div>
The js code related to the problem is here :
const slider = document.querySelector("#image-comparison-slider");
const sliderImgWrapper = document.querySelector("#image-comparison-slider .img-wrapper");
const sliderHandle = document.querySelector("#image-comparison-slider .handle");
slider.addEventListener("mousemove", sliderMouseMove);
slider.addEventListener("touchmove", sliderMouseMove);
function sliderMouseMove(event) {
if(isSliderLocked) return;
const sliderLeftX = slider.offsetLeft;
const sliderWidth = slider.clientWidth;
const sliderHandleWidth = sliderHandle.clientWidth;
let mouseX = (event.clientX || event.touches[0].clientX) - sliderLeftX;
if(mouseX < 0) mouseX = 0;
else if(mouseX > sliderWidth) mouseX = sliderWidth;
sliderImgWrapper.style.width = `${((1 - mouseX/sliderWidth) * 100).toFixed(4)}%`;
sliderHandle.style.left = `calc(${((mouseX/sliderWidth) * 100).toFixed(4)}% - ${sliderHandleWidth/2}px)`;
}
let isSliderLocked = false;
slider.addEventListener("mousedown", sliderMouseDown);
slider.addEventListener("touchstart", sliderMouseDown);
slider.addEventListener("mouseup", sliderMouseUp);
slider.addEventListener("touchend", sliderMouseUp);
slider.addEventListener("mouseleave", sliderMouseLeave);
function sliderMouseDown(event) {
if(isSliderLocked) isSliderLocked = false;
sliderMouseMove(event);
}
function sliderMouseUp() {
if(!isSliderLocked) isSliderLocked = true;
}
function sliderMouseLeave() {
if(isSliderLocked) isSliderLocked = false;
}
Here are some functions that helps me to move the slider above the images to be able to see the differences between them.
It works properly but I need to scroll in the parent div and I can't do that.
I created class based functions in my project. Scroll class is to create smooth scroll behaviour using translateY property. The second one called SideNavigation generate bullet list navigation based on projects elements length. I want to achive smooth scroll effect also on click on those bullets, but can't find solution for this. Code snippet included. Hope anyone can help with this.
const config = {
ease: 0.1,
current: 0,
previous: 0,
rounded: 0
}
class Scroll {
constructor(velocity) {
this.velocity = velocity
}
setBodyHeight() {
document.body.style.height = document.getElementById('projects').getBoundingClientRect().height + 'px'
}
isMobile() {
return 'ontouchstart' in document.documentElement && navigator.userAgent.match(/Mobi/)
}
onScroll() {
config.current = window.scrollY
config.previous += (config.current - config.previous) * config.ease
config.rounded = Math.round(config.previous * 100) / 100
const difference = config.current - config.rounded
const acceleration = difference / document.body.clientWidth
const velocity = +acceleration
const skew = velocity * this.velocity
const element = document.getElementById('projects')
this.isMobile() ? element.style.transform = `skewY(${skew}deg)` : element.style.transform = `translateY(-${config.rounded}px)`
requestAnimationFrame(() => this.onScroll())
}
}
class SideNavigation {
constructor() {
createBullets()
}
}
function createBullets() {
const bulletWrapper = document.createElement('div')
const bulletList = document.createElement('ul')
bulletWrapper.classList.add('side-nav')
for (let i = 0; i < document.getElementsByClassName('project').length; i++) {
const bullet = document.createElement('li')
bullet.setAttribute('id', '' + i)
bullet.addEventListener('click', event => {
console.log(document.getElementsByClassName('project')[parseInt(event.target.id)].offsetTop)
// scroll to current section
})
bulletList.append(bullet)
bulletWrapper.append(bulletList)
document.body.append(bulletWrapper)
}
}
window.addEventListener('load', () => {
const scrollInstance = new Scroll(8, true)
const sideNav = new SideNavigation()
scrollInstance.setBodyHeight()
requestAnimationFrame(() => scrollInstance.onScroll())
})
html,
body {
height: 100%;
}
main {
position: fixed;
left: 0;
top: 0;
height: 100%;
width: 100%;
overflow: hidden;
}
#projects {
width: 80%;
}
.project {
height: 500px;
width: 100%;
background: #333;
color: #fff;
}
.side-nav {
position: fixed;
right: 20px;
top: 20%;
z-index: 10;
}
.side-nav li {
width: 30px;
height: 30px;
display: block;
background: #333;
margin-bottom: 20px;
border-radius: 50%;
cursor: pointer;
}
<body>
<main>
<div id="projects">
<div class="project">
<p>Some text example</p>
</div>
<div class="project">
<p>Some text example</p>
</div>
<div class="project">
<p>Some text example</p>
</div>
<div class="project">
<p>Some text example</p>
</div>
<div class="project">
<p>Some text example</p>
</div>
</div>
</main>
</body>
I hope I understood your question correctly. Is this what you're looking for?
const config = {
ease: 0.1,
current: 0,
previous: 0,
rounded: 0
}
class Scroll {
constructor(velocity) {
this.velocity = velocity
}
setBodyHeight() {
document.body.style.height = document.getElementById('projects').getBoundingClientRect().height + 'px'
}
isMobile() {
return 'ontouchstart' in document.documentElement && navigator.userAgent.match(/Mobi/)
}
onScroll() {
config.current = window.scrollY
config.previous += (config.current - config.previous) * config.ease
config.rounded = Math.round(config.previous * 100) / 100
const difference = config.current - config.rounded
const acceleration = difference / document.body.clientWidth
const velocity = +acceleration
const skew = velocity * this.velocity
const element = document.getElementById('projects')
this.isMobile() ? element.style.transform = `skewY(${skew}deg)` : element.style.transform = `translateY(-${config.rounded}px)`
requestAnimationFrame(() => this.onScroll())
}
}
class SideNavigation {
constructor() {
createBullets()
}
}
function createBullets() {
const bulletWrapper = document.createElement('div')
const bulletList = document.createElement('ul')
bulletWrapper.classList.add('side-nav')
for (let i = 0; i < document.getElementsByClassName('project').length; i++) {
const bullet = document.createElement('li')
bullet.setAttribute('id', '' + i)
bullet.addEventListener('click', event => {
console.log(document.getElementsByClassName('project')[parseInt(event.target.id)].offsetTop)
// scroll to current section
window.scrollBy(0, document.getElementsByClassName('project')[parseInt(event.target.id)].offsetTop - window.pageYOffset);
})
bulletList.append(bullet)
bulletWrapper.append(bulletList)
document.body.append(bulletWrapper)
}
}
window.addEventListener('load', () => {
const scrollInstance = new Scroll(8, true)
const sideNav = new SideNavigation()
scrollInstance.setBodyHeight()
requestAnimationFrame(() => scrollInstance.onScroll())
})
html,
body {
height: 100%;
}
main {
position: fixed;
left: 0;
top: 0;
height: 100%;
width: 100%;
overflow: hidden;
}
#projects {
width: 80%;
}
.project {
height: 500px;
width: 100%;
background: #333;
color: #fff;
}
.side-nav {
position: fixed;
right: 20px;
top: 20%;
z-index: 10;
}
.side-nav li {
width: 30px;
height: 30px;
display: block;
background: #333;
margin-bottom: 20px;
border-radius: 50%;
cursor: pointer;
}
<body>
<main>
<div id="projects">
<div class="project">
<p>Some text example</p>
</div>
<div class="project">
<p>Some text example</p>
</div>
<div class="project">
<p>Some text example</p>
</div>
<div class="project">
<p>Some text example</p>
</div>
<div class="project">
<p>Some text example</p>
</div>
</div>
</main>
</body>
The scrollBy function's second parameter allows to modify the window's Y axis. But as we want each bullet to jump to the start of it's project, we decrease the current Y axis of the page from the Y axis of the project window.scrollBy(0, document.getElementsByClassName('project')[parseInt(event.target.id)].offsetTop - window.pageYOffset).
I created a basic voting system for a comment ratings bar. I'm trying to access the previous Sibling Element to update the votes but it's not working properly. IAre you're supposed to use event.currentTarget or event.target? Where did I go wrong? Thank you.
https://jsfiddle.net/donfontaine12/bm9njcLt/46/#&togetherjs=qocecyJqyy
HTML
<div id="comment_ratings_bar">
<div id="comment_rating_sign">+</div>
<div id="comment_rating_num">0</div>
<div id="comment_rating_percentage">[100.00%] </div>
<div class="green_up_arrow"></div>
<div class="red_down_arrow"></div>
</div>
<div id="comment_ratings_bar">
<div id="comment_rating_sign">+</div>
<div id="comment_rating_num">0</div>
<div id="comment_rating_percentage">[100.00%] </div>
<div class="green_up_arrow"></div>
<div class="red_down_arrow"></div>
</div>
<div id="comment_ratings_bar">
<div id="comment_rating_sign">+</div>
<div id="comment_rating_num">0</div>
<div id="comment_rating_percentage">[100.00%] </div>
<div class="green_up_arrow"></div>
<div class="red_down_arrow"></div>
</div>
<div id="comment_ratings_bar">
<div id="comment_rating_sign">+</div>
<div id="comment_rating_num">0</div>
<div id="comment_rating_percentage">[100.00%] </div>
<div class="green_up_arrow"></div>
<div class="red_down_arrow"></div>
</div>
CSS
#comment_ratings_bar {
width: 30%;
margin: 0px 20px;
padding: 0px 20px;
font-size: 110%;
font-weight: bolder;
font-family: 'B612 Mono', monospace;
color: lime;
background-color: black;
border: 0px solid black;
display: flex;
flex-direction: row;
justify-content: center;
}
.green_up_arrow {
display: flex;
flex-direction: row;
width: 0;
height: 0;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-bottom: 10px solid lime;
cursor: pointer;
margin: 0em 0.25em;
}
.red_down_arrow {
display: flex;
flex-direction: row;
width: 0;
height: 0;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-top: 10px solid red;
cursor: pointer;
margin: 0em 0.25em;
}
JavaScript
window.onload = function() {
let commentUpvotes = 0;
let commentDownvotes = 0;
let totalCommentVotes = commentUpvotes + commentDownvotes;
let commentRatingsBarAll = document.querySelectorAll("#comment_ratings_bar");
for (let c of commentRatingsBarAll) {
c.lastElementChild.previousElementSibling.addEventListener("click", updateCommentVotes);
c.lastElementChild.addEventListener("click", updateCommentVotes);
}
function updateCommentVotes(e) {
let siblings = getSiblings(e);
let sign = siblings[0].textContent;
let number = siblings[1].textContent;
let percentage = siblings[2].textContent;
if (sign && number && percentage) {
let actualNumber = parseFloat(number.replace(/,/g, ''));
if (e.target.className == "green_up_arrow") {
actualNumber++; commentUpvotes++; totalCommentVotes++;
} else {
actualNumber--; commentDownvotes++; totalCommentVotes++;
}
if (actualNumber < 0) { sign.replace("+", ""); }
percentage = "["
+ parseFloat((commentUpvotes / totalCommentVotes) * 100).toFixed(2) +"%]";
number = actualNumber.toLocaleString();
}
}
function getSiblings(element) {
if (element) {
let siblings = [];
let sibling = element.parentNode.firstElementChild;
while(sibling) {
if (sibling.nodeType === 1 && sibling !== element) {
siblings.push(sibling);
sibling = sibling.nextElementSibling;
}
}
return siblings;
}
}
}
Everything's working but inside the updateCommentVotes function, I should have been referencing the actual divs containing the textContent instead of the local variables (sign, number & percentage).
EDIT: It's a partial fix, I need each individual comment bar to refer to its own sign, number and percentage. It seems they all share the same number values. Any tips are appreciated. Although, I believe its because I hard coded the values from siblings. Thank you.
Check the code here: https://jsfiddle.net/donfontaine12/bm9njcLt/46/#
JavaScript
window.onload = function() {
let commentUpvotes = 0;
let commentDownvotes = 0;
let totalCommentVotes = commentUpvotes + commentDownvotes;
let commentRatingsBarAll = document.querySelectorAll("#comment_ratings_bar");
for (let c of commentRatingsBarAll) {
c.lastElementChild.previousElementSibling.addEventListener("click", updateCommentVotes);
c.lastElementChild.addEventListener("click", updateCommentVotes);
}
function updateCommentVotes(e) {
let siblings = getSiblings(e);
let sign = siblings[0].textContent;
let number = siblings[1].textContent;
let percentage = siblings[2].textContent;
if (sign && number && percentage) {
let actualNumber = parseFloat(number.replace(/,/g, ''));
if (e.target.className == "green_up_arrow") {
actualNumber++; commentUpvotes++; totalCommentVotes++;
} else {
actualNumber--; commentDownvotes++; totalCommentVotes++;
}
if (actualNumber < 0) { siblings[0].textContent.replace("+", ""); }
siblings[2].textContent = "["
+ parseFloat((commentUpvotes / totalCommentVotes) * 100).toFixed(2) +"%]";
siblings[1].textContent = actualNumber.toLocaleString();
}
}
function getSiblings(element) {
let siblings = [];
let sibling = element.target.parentNode.firstElementChild;
while(sibling) {
if (sibling.nodeType === 1 && sibling !== element) {
siblings.push(sibling);
sibling = sibling.nextElementSibling;
}
}
return siblings;
}
}
Working on Jquery (Toggle Slider).
On load, the left button should be disabled (currently not working).
After first click, the right button then left button should be enabled.
When we get to the last slide, the right button should be disabled.
(Currently not working)
When the slide goes to first position the slide shouldn't move again
the same for last slide also
Here is my jQuery code for reference.
$(".leftBtn").click(function(e) {
goRight();
});
$(".rightBtn").click(function(e) {
goLeft();
});
function goRight() { // inner stuff slides left
var initalLeftMargin = $(".innerLiner").css('margin-left').replace("px", "") * 1;
var newLeftMargin = (initalLeftMargin - 204); // extra 2 for border
$(".innerLiner").animate({
marginLeft: newLeftMargin
}, 500);
}
function goLeft() { // inner stuff slides right
var initalLeftMargin = $(".innerLiner").css('margin-left').replace("px", "") * 1;
var newLeftMargin = (initalLeftMargin + 204); // extra 2 for border
if (newLeftMargin >= 0){
$(".leftBtn").css("display", "none");
} else {
$(".leftBtn").css("display", "block");
}
$(".innerLiner").animate({
marginLeft: newLeftMargin
}, 500);
}
* {
Box-sizing: Border-box
}
.mycontainer {
white-space: nowrap;
overflow-x: hidden;
width: 204px;
}
.box {
display: inline-block;
border: 2px black solid;
padding: 20px;
width: 200px;
height: 100px;
vertical-align: top;
background-color: pink;
}
.box2 {
background-color: yellow;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<input class="rightBtn" type="button" value="Left">
<div class="mycontainer">
<div class="innerLiner">
<span class="box">
This is box1
</span>
<span class="box">
This is box2
</span>
<span class="box box2">
This is box3
</span>
</div>
</div>
<input class="leftBtn" type="button" value="Right">
You are moving to the correct direction. Here are some tips to fix the code:
Move the button update code to a function to make it easy to update and call.
Show both buttons by default, and hide the correct one depending on new margin.
Call the function with initial margin to disable the correct initial button, before user click anything.
In short,
function updateButtons( newLeftMargin ) {
$(".leftBtn,.rightBtn").show(); // Show both buttons by default
if ( newLeftMargin >= 0 )
$(".rightBtn").hide();
if ( newLeftMargin <= -408 )
$(".leftBtn").hide();
}
updateButtons(0)
Below is a complete snippet. Note that I took the liability to lightly optimise your other code.
function goRight() { // inner stuff slides left
var initalLeftMargin = parseInt( $(".innerLiner").css('margin-left') );
var newLeftMargin = (initalLeftMargin - 204); // extra 2 for border
updateButtons( newLeftMargin );
$(".innerLiner").animate({
marginLeft: newLeftMargin
}, 500);
}
function goLeft() { // inner stuff slides right
var initalLeftMargin = parseInt( $(".innerLiner").css('margin-left') );
var newLeftMargin = (initalLeftMargin + 204); // extra 2 for border
updateButtons( newLeftMargin );
$(".innerLiner").animate({
marginLeft: newLeftMargin
}, 500);
}
function updateButtons( newLeftMargin ) {
$(".leftBtn,.rightBtn").show(); // Show both buttons by default
if ( newLeftMargin >= 0 )
$(".rightBtn").hide();
if ( newLeftMargin <= -408 )
$(".leftBtn").hide();
}
updateButtons(0)
$(".leftBtn").click( goRight );
$(".rightBtn").click( goLeft );
* {
Box-sizing: Border-box
}
.mycontainer {
white-space: nowrap;
overflow-x: hidden;
width: 204px;
}
.box {
display: inline-block;
border: 2px black solid;
padding: 20px;
width: 200px;
height: 100px;
vertical-align: top;
background-color: pink;
}
.box2 {
background-color: yellow;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<input class="rightBtn" type="button" value="Left">
<div class="mycontainer">
<div class="innerLiner">
<span class="box">
This is box1
</span>
<span class="box">
This is box2
</span>
<span class="box box2">
This is box3
</span>
</div>
</div>
<input class="leftBtn" type="button" value="Right">