I am experiencing weird behaviour from my jQuery because my laptop screen size/resolution is somewhat smaller than an ordinary PC screen.
What I am trying to do is position a dropdown depending on where it'd fit best: if the event target is too close to the bottom of the window then you show the dropdown above the event target, and vice versa.
As seen here, if too close to the bottom, dropdown appears at the top: http://jsfiddle.net/u7n6tLgo/14/
And here, if too close to the top, dropdown appears at the bottom: http://jsfiddle.net/u7n6tLgo/15/
$(this).on("click", ".selector > *[data-action='open']", function(event) {
const selector = $(this).parent(".selector");
const container = selector.find(".container");
const title = selector.find(".title");
container.toggle();
let windowHeight = $(window).height();
let titleOffsetTop = title.offset().top;
let titleHeight = title.height();
let containerHeight = container.height();
title.toggleClass("selected");
if(windowHeight < (titleOffsetTop + titleHeight + containerHeight) && containerHeight < titleOffsetTop) {
container.css("top", (-containerHeight-titleHeight));
} else {
container.css("top", 50);
}
});
I'm using a 15" Laptop, and for me the dropdown is always at the top, unless I zoom out my browser window to <80% (outside of the fiddle in my private project obviously; in the fiddle it is working as expected). I asked a friend of mine to try the code on his 24" screen and there it works fine.
For some reason the title.offset().top is above 1000, more than my window height, which my friend pointed out as weird. Not sure if that's why it's being weird. HTML and CSS is in the fiddles.
Why is it behaving like this? And more importantly, how do I fix it or work around it?
Edit:
<div class="selector">
<div class="title" data-action="open">Open</div>
<div class="container">
<ul class="itemList">
<li>Hello</li>
<li>Hello</li>
<li>Hello</li>
<li>Hello</li>
<li>Hello</li>
<li>Hello</li>
</ul>
</div>
</div>
Related
I am looking for a way to determine if any part of a div is touching the top of the viewport and fix an item in that div to the top of the viewport using vanilla javascript.
I have been able to sort out how to determine if the div is touching the top of the viewport and trigger changes to the div's style. But for some reason when I change the div's position: absolute to position: fixed the div fixes to the top of the document, not to the top of the viewport, hence is not visible.
My js
function touchTop() {
var div = $('itin');
var rect = div.getBoundingClientRect();
var y = rect.top;
var h = rect.bottom;
if ((y < 0) && (h > 0)) {
document.getElementById('seemore').style.position = 'fixed';
document.getElementById('seemore').style.top = '45%';
} else {
document.getElementById('seemore').style.position = 'absolute';
document.getElementById('seemore').style.top = '66px';
}
}
window.addEventListener('scroll', touchTop);
The basic div HTML
<div id="itin" class="container">
<div class="sp20"></div>
<div class="text rgt">
<h3>your daily adventures</h3>
<p>blah blah blah</p>
</div>
<div id="seemore" class="ghstbtn">See More</div>
</div>
And the basic initial CSS
#seemore {
width: auto;
position: absolute;
top: 66px;
right: 20px;
}
To clarify further: My problem that needs solving is that when javascript changes the style.position to fixed the #seemore div gets positioned such that the 'top' value is measured from the top of the document, not from the top of the viewport. So basically not visible in the viewport.
If I understand you correctly, I think your problem lays here:
if ((y < 0) && (h > 0))
When the container div hits the top of the document, position to "seemore" is set to fixed, but as fast as the bottom of the div hits the top of the document,
"h === 0" and the position is again set to absolute.
Try this.
const seeMore = document.getElementById('seemore');
const div = document.getElementById('itin');
window.addEventListener('scroll', checkBoundries);
function checkBoundries() {
var rect = div.getBoundingClientRect();
var y = rect.top;
var h = rect.bottom;
if (y < 0) {
seeMore.style.position = 'fixed';
seeMore.style.top = '45%';
} else {
seeMore.style.position = 'absolute';
seeMore.style.top = '66px';
}
}
Turns out to be related to a filter applied to a parent of a parent of a parent of a ....
Found some old questions regarding issues with transformations and position:fixed dating back some 5 to 7 years ago. And even though many mentioned filing issue reports with the browser makers, it seems the problem has never been addressed and resolved. One comment mentioned filters could also cause the issue.
Moved the filter to a separate class which is now added and removed, rather than applying the filter directly to the div. And everything works as expected.
I'm planning to recreate the Medium.com like sidebar. This is what I have now but it's far from over.
Open the JSFiddle below to understand better; I am looking to do the following:
When you scroll down, it suddenly sticks to the bottom. How do I make it stick gradually as you scroll?
Because it uses position: fixed, it moves towards the right side without respecting the layout. How do I fix this?
When you scroll up and there's less space, it overlaps with the header as shown in the screenshot. Again, most likely because position: fixed is used.
How do I fix this? I know there's sticky-kit that does the work but I can't use any plugin.
HTML:
<div class="container">
<div class="row">
<div class="col-xs-12">
Header and Menu is placed here.
<hr />
</div>
<div class="col-xs-8">
<p>This is the main body which the user would be scrolling infinitely as more data gets loaded on scroll.</p>
</div>
<div class="col-xs-4">
<div id="sidebar">
<p>
This is the sidebar which I want to stop when it reaches the bottom like the one shown in medium dot com
</p>
</div>
</div>
</div>
</div>
Javascript:
function scrollCheck() {
var $right = $('#sidebar'),
scrollTop = $(window).scrollTop(),
windowHeight = $(window).height(),
docHeight = $(document).height(),
rightHeight = $right.height(),
distanceToTop = rightHeight - windowHeight,
distanceToBottom = scrollTop + windowHeight - docHeight;
if (scrollTop > distanceToTop) {
$right.css({
'position': 'fixed',
'bottom': (scrollTop + windowHeight > docHeight ? distanceToBottom + 'px' : '0px')
});
}
else {
$right.css({
'position': 'relative',
'bottom': 'auto'
});
}
}
$(window).bind('scroll', scrollCheck);
JSFIDDLE
I'll answer what questions of yours I could. Here is the edited Fiddle first.
As for your questions:
The sudden sticking to the bottom is caused because the element isn't the full length of the page so sticking it to the bottom with a fixed position will cause it to jump there. Whereas with the change I made so it sticks to the top there won't have this jump, as the element is at the top of the screen when scrolling so it can be discreetly fixed there.
This was because the element didn't have a fixed width and setting position: fixed; means that the elements width is no longer set by the parent, but the view port. So it expands to fill all the width in the view port it can.
This was happening because the position: fixed; was never removed when scrolling back above the elements original position, the updated if statement in the Js now removes the class with position: fixed; when scrolling above its original location.
A more detailed look into what I changed.
I added a CSS class so .toggleClass could be used to make the function cleaner.
.docked-sidebar {
position: fixed;
top: 0;
}
I also changed the conditions for the if statement so that they worked. Using .offset().top; to get the distance between the sidebar and the top of the page, while removing most of the other variables as they weren't needed. Finally I created bool variable(isDocked) so that the same condition isn't triggered multiple times.
var $right = $('#sidebar'),
isDocked = false,
initOffsetTop = $right.offset().top;
function scrollCheck() {
var scrollTop = $(window).scrollTop(),
offsetTop = $right.offset().top;
if (!isDocked && ((offsetTop - scrollTop) < 0)) {
$right.toggleClass("docked-sidebar");
isDocked = true;
} else if (isDocked && scrollTop <= initOffsetTop) {
$right.toggleClass("docked-sidebar");
isDocked = false;
}
}
For sticking to the bottom and then to the top exactly like the example website I recommend checking this question.
I have read similar questions related to my issue, but have not come across a solution. I am working on a menu created by another developer for a new client of mine. The client does not wish to use a jQuery, which I believe would solve my issue, so I am hoping to find an adjustment to my jQuery to get this bug fixed. I have a 2-level menu, and on a mobile device, the menu collapses upon scrolling. I can not figure out how to prevent this from happening, as it does not occur when I test on my PC even at the same width. The jQuery used is triggered on .mouseenter and .mouseleave events, so I believe it might have something to do with that. Anyone have an idea as to how to prevent the menu from collapsing upon mobile scroll without a plugin?
Below is the jQuery used:
var $navItem = $("#main_nav").children("#nav").children("li").children("a");
var $subItems = $($navItem).siblings("ul");
$(function(){
$($subItems).hide();
var width = $(window).width(), height = $(window).height();
if ((width <= 570))
{
$("#main_nav").children("#nav").hide();
}
else
{
$("#main_nav").children("#nav").show();
}
});
$(window).on('load resize', function(){
var width = $(window).width(), height = $(window).height();
if ((width <= 570))
{
$("#main_nav").children("#nav").hide();
}
else
{
$("#main_nav").children("#nav").show();
}
});
$($navItem).mouseenter(function(e){
if ($(window).width() > 570) {
//show submenu
$(this).siblings("ul").show();
}
});
$($navItem).mouseleave(function(){
if ($(window).width() > 570) {
//hide submenu
$(this).siblings("ul").hide();
}
});
$($subItems).mouseenter(function(){
$(this).closest("ul").show();
$(this).closest("ul").siblings("a").addClass("active");
});
$($subItems).mouseleave(function(){
$(this).closest("ul").hide();
$(this).closest("ul").siblings("a").removeClass("active");
});
$(".drop-down-arrow").click(function(e){
e.preventDefault();
$(this).toggleClass("flip");
$(this).parent().siblings("ul").toggle();
});
$(".drop-down-arrow").each(function(){
if($(this).parent("a").siblings("ul").size() <= 0)
{
$(this).closest('.drop-down-arrow').css('display','none !important;');
}
});
HTML:
<ul id="nav" style="display: none;">
<li>Item 1<span class="drop-down-arrow"><img src="/media/199707/arrow-down.png" alt="drop down arrow"></span>
<ul class="child" style="display: none;">
<li>Transmission Repair</li><li>Brake Repair</li>
<li>Engine Repair</li><li>Air Conditioning</li>
<li>Auto Glass Repair</li><li>Window Tinting</li>
</ul>
</li>
<li>Item 2</li>
</ul>
If I understand your problem correctly, .mouseenter sure can cause this, because a touch/drag on mobile is also recognized as a mouse event in websites.
I see you try to filter mobile devices with checking the window's width. It may be wrong on tablets or phones with bigger resolutions.
Maybe you could try using this method in you mouse events:
What is the best way to detect a mobile device in jQuery?
I have an issue with a jquery function. You can see a working demo here - http://dev.sreejesh.in/menuissue/ . As you can see when the user scrolls down to the page, I have written a jQuery function(which will triger on scroll) to check scroll pixel. When the browser scrolls to a certain pixel(height of the sidemenu block), the Menu block will stay fixed & rest of the content scrolls as normal.
The functionality is working now, however the problem is menublocks makes a jumps when this function runs. I think this is because of the delay in running the function. Hope you guys have any nice trick to fix this.
I used an if/else function to check the scroll pixel, so when the scrolled pixel is greater than menublock height it will add a class "fixed" .
I use the following code.
HTML
<div id="globalwrapper">
<div id="menubar">
---- Menu List items-----
</div>
<div id="mainblock">
----Main content area----
</div>
</div>
jQuery
$(document).ready(function(){
$(window).scroll(function() {
adjustScroll();
});
});
function adjustScroll(){
var windowHeight = $(window).height();
var menublockHeight = $('#menubar').height();
var scrollValue = $(document).scrollTop();
var posValue = menublockHeight - windowHeight;
var menuStatus = $('#menubar').css('left');
$('#menubar').css('minHeight', windowHeight);
$('#menubar').css('height', menublockHeight);
console.log(menuStatus);
$(document).scroll(function() {
if(menuStatus == '0px') {
if(scrollValue > posValue){
$('#menubar').addClass('fixed');
$('#menubar').css('marginTop', -posValue);
}else {
$('#menubar').removeClass('fixed');
$('#menubar').css('marginTop', '0px');
}
}
});
}
I think only CSS can solve this issue, add this style:
#menubar{
position: fixed;
}
just test on Google Chrome,you can have a try.
On the right side of my page I have a list of sponsors.
My active page area (left side) height varies from page to page, depending on the story it contains every time.
I want the list's height to match the live pages height, so I thought I'd always show the main sponsors, and for the rest of them, I'd hide them, and show exactly as many as I can each time.
My markup looks like this:
<div id="xorigoi">
<a class="basic"><img src="/views/images/adjustable/sideXorigoi/1.png"></a>
<a class="basic"><img src="/views/images/adjustable/sideXorigoi/2.png"></a>
<a class="basic"><img src="/views/images/adjustable/sideXorigoi/3.png"></a>
<a class="rest"><img src="/views/images/adjustable/sideXorigoi/4.png"></a>
<a class="rest"><img src="/views/images/adjustable/sideXorigoi/5.png"></a>
<a class="rest"><img src="/views/images/adjustable/sideXorigoi/6.png"></a>
<a class="rest"><img src="/views/images/adjustable/sideXorigoi/7.png"></a>
<a class="rest"><img src="/views/images/adjustable/sideXorigoi/8.png"></a>
</div>
Every image is a link to each sponsor's site. Every image has it's own height.
Elements that have the class .rest are hidden using display: none
I'm trying to calculate if adding the new image will make the list longer than the page, but since the elements are hidden, offsetHeight = 0.
What can I do?
My javascript/jquery code so far looks like this:
$(
function(){
var containerHeight = $('#mainPageContainer')[0].offsetHeight; // total height of page
var xorigoi = $('#mainRightSide .rest'); // select the sponsors
var newHeight = 1062; // this is the right side height I am already using
$.each( xorigoi , function( index ){
if( newHeight + heightOfNewElement > containerHeight ){
return false; // break each
}
xorigoi.eq(index).css('display','block'); // display the sponsor
newHeight = newHeight + heightOfNewElement;
})
}
)
So bottomline, how can I get heightOfNewElement in the function above?
Since JavaScript is single-threaded, the browser will not redraw while it is executing. As such, you can safely set the elements to display:block, measure them, then hide them again and the user will be none the wiser.
Instead of display none, you could consider moving your elements outside the viewport. For example:
.rest {position:absolute;top:-9999px;}
Guess this will help you.
console.log(getDimensions($('#myElement')));
function getDimensions(element){
var tempElement = $(element).clone();
$(tempElement).css('display','block').css('visibility','hidden');
$(document.body).append(tempElement);
var obj = new Object();
obj.width = $(tempElement).width();
obj.height = $(tempElement).height();
$(tempElement).remove();
return obj;
}