EDIT: looks like I was approaching this from the wrong direction, will see if I can close this. Future reading:
Get mouse wheel events in jQuery?
I'm trying to create a scrollable <div> element (on a horizontal plane); not there with the maths yet but I'm having trouble with something more basic - the scroll event doesn't seem to be registering. Am I missing anything obvious?
I've taken out all the CSS associated with the <div> barring what you see in the samples below:
<div id="about-carousel" carousel-offset="0" style="height:20rem; background-color:red;">
</div>
$(document).ready(function() {
handleCarousels(['#about-carousel']);
});
function handleCarousels(idArray) {
for(var n in idArray) {
var target = idArray[n];
// this works
$(target).click(function() {
console.log('clicked');
});
// this doesn't
$(target).scroll(function(e) {
e.stopPropagation();
console.log('scrolls');
});
}
}
According to this answer the element needs to have overflow: scroll set, but that makes no difference. Even if I fill the <div> with images, it doesn't trigger the .scroll event at all.
Any ideas?
UPDATE BEFORE POSTING: the 'scrolls' message is being output to console if I set the div to overflow: scroll and fill it with images, but only when I drag the scroll bar. I thought it was meant to capture mouse events too?
I'm using jQuery 1.11.1.
Related
Introduction
I'm using Semantic-UI's sidebar functionality, which gives you a button that triggers a sidebar that pushes the content from the left (in this case).
I want to unfold that same sidebar by hovering with the mouse on the left side. I realize there are several ways to do it (as these often do. Maybe just checking the X position of the mouse would work but that's beside the point); I chose to create a transparent div on the left side and make its :hover pseudo-class to trigger the sidebar:
// create sidebar and attach to menu open
$('.ui.sidebar').sidebar('attach events', '.toc.item');
// hover transparent div to trigger the sidebar too:
$('.sidebar-trigger').hover(function() {
$('.ui.sidebar').sidebar('show')
});
// hide() and show() the sidebar accordingly to use the sidebar:
$('.ui.sidebar').sidebar('setting', {
onShow: function() {
$('.sidebar-trigger').hide();
},
onHidden: function() {
$('.sidebar-trigger').show();
}
});
Problem
Now, it all works except for one occasion: when you don't stop moving the mouse as the sidebar opens. I've looked at $(document).on('transitionend', function(event) { ... } and that mouse effectively prevents the transition to finish.
Resources
I've put a blue background on my .sidebar-trigger and made a small video/gif so as to be clearer.
I moved the mouse like a crazy creature but with natural gestures the problem occurs as well.
I'm using Semantic-UI's guide on this thing: http://semantic-ui.com/modules/sidebar.html#/settings (I've also tried onVisible and onHide with no luck)
This is a OSX Yosemite 10.10.3 running Chrome 45.0.2454.101 (64-bit)
jsfiddle with the problem at hand
PS: It seems it might be an OSX Chrome bug?
I would try using one and mouseover:
$('.sidebar-trigger').one('mouseover', function() {
$('.ui.sidebar').sidebar('show')
});
Then, when it has finished animating, reattach the event:
$(document).on('transitionend', function(event) {
$('.sidebar-trigger').one('mouseover', function() {
$('.ui.sidebar').sidebar('show')
});
});
I think what is happening is that the hover event is getting called multiple times - every time the element is hovered, then goes over a child element, and then goes back over the hover element, and things are getting mixed up at some point. So you need to only call show if it's not already shown.
Here is a working example: Fiddle
I believe when the element was hovered, it was adding a classes 'uncover' and 'visible', and another called 'animating' which wouldn't fire until the mouse stopped moving. I changed the jQuery slightly to only add classes 'uncover' and 'visible', and it still animated okay. However, the body was pushing right too far by 175px, so I had to edit the class that was causing that (noted below) from 260px to 85px. This DOES get the menu acting properly though from my understanding.
$('.sidebar-trigger').mouseenter(function() {
$('.ui.sidebar').addClass('uncover, visible');
$('body').addClass('mleft175');
});
$('body').click(function() {
$('.ui.sidebar').removeClass('uncover, visible');
$('body').removeClass('mleft175');
});
and then add overriding class
.ui.visible.left.sidebar ~ .pusher
{
-webkit-transform: translate3d(85px, 0, 0);
transform: translate3d(85px, 0, 0);
}
Right now it is set to hide the menu when the body is clicked. Alternatively you can hide it when the mouse leaves the sidebar menu:
$('.ui.sidebar').mouseleave(function(){
$(this).removeClass('uncover, visible')
});
Ok, my first answer was (of course) way too much work for what it really needed. The onVisible seems to work perfectly. Was that not working for you? Demo HERE
Simply change 'onShow' to 'onVisible' in your sidebar setting:
$('.ui.sidebar').sidebar('setting', {
onVisible: function() {
$('.sidebar-trigger').hide();
},
onHidden: function() {
$('.sidebar-trigger').show();
}
});
As shown on the Semantic UI site, the onVisible fires when the animating starts. The onShow fires when the animating finishes. So what you were doing was hiding that blue / transparent bar when the animation was finally done (the .animating class noted in my previous answer), as opposed to when it starts. If you need further explanation please let me know.
...without limiting the scroll inside the iframe or the need to specifically name/tag all scrollable elements.
Imagine google maps widget embedded in parent page. When you zoom in the widget you don't want the parent page to scroll, obviously.
I thought an answer to my previous question solved the problem:
While scrolling inside an iframe, the body doesn't know anything about
what happens there. But when iframe scroller reach the bottom or the
top, it pass scrolling to body.
Cancel the event that propagates from the iframe.
But the solution does not work in Firefox because Firefox will not - by design - propagate events captured by iframe to the parent page, yet strangely it will scroll the parent page. See jsfiddle here.
$('body').bind('mousewheel DOMMouseScroll', onWheel);
function onWheel (e){
if (e.target === iframe)
e.preventDefault();
console.log(e);
}
So, how do I prevent page from scrolling when user zooms content in embedded iframe, in Firefox?
Since it is a bug in Firefox, the workaround is to work directly with the scroll event, instead of the mousewheel / DOMMouseScroll ones.
The way I did: When user enters the mouse over the iframe, I set a flag to true, and when he leaves the mouse out there, I set it back to false.
Then, when user tries to scroll, but the mouse arrow is inside the iframe, I prevent the parent window scrolling. But, unfortunately, you can't prevent the window scrolling with the usual e.preventDefault() method, so we still need another workaround here, forcing the window to scroll exactly to the X and Y positions it was already before.
The full code:
(function(w) {
var s = { insideIframe: false }
$(iframe).mouseenter(function() {
s.insideIframe = true;
s.scrollX = w.scrollX;
s.scrollY = w.scrollY;
}).mouseleave(function() {
s.insideIframe = false;
});
$(document).scroll(function() {
if (s.insideIframe)
w.scrollTo(s.scrollX, s.scrollY);
});
})(window);
I've created an immediately executed function to prevent defining the s variable in the global scope.
Fiddle working: http://jsfiddle.net/qznujqjs/16/
Edit
Since your question was not tagged with jQuery (although inside it, you've showed a code using the library), the solution with vanilla JS is as simple as the above one:
(function(w) {
var s = { insideIframe: false }
iframe.addEventListener('mouseenter', function() {
s.insideIframe = true;
s.scrollX = w.scrollX;
s.scrollY = w.scrollY;
});
iframe.addEventListener('mouseleave', function() {
s.insideIframe = false;
});
document.addEventListener('scroll', function() {
if (s.insideIframe)
w.scrollTo(s.scrollX, s.scrollY);
});
})(window);
Given all the prerequisites, I think the following is the sanest way to make this work in Firefox.
Wrap your iframe with a div which is a little bit shorter to enable vertical scrolling in it:
<div id="wrapper" style="height:190px; width:200px; overflow-y: auto; overflow-x: hidden;">
<iframe id="iframeid" height="200px" width="200px" src="about:blank">
</iframe>
</div>
Now you can center the iframe vertically and re-position it every time
the wrapper receives a scroll event (it will occur when a user tries to scroll away at frame edges):
var topOffset = 3;
wrapper.scrollTop(topOffset);
wrapper.on("scroll", function(e) {
wrapper.scrollTop(topOffset);
});
Combine this with your previous fix for Chrome, and it should cover all major browsers. Here is a working example - http://jsfiddle.net/o2tk05ab/5/
The only outstanding issue will be the visible vertical scrollbar on a wrapper div. There are several ways to go about it, for instance - Hide scroll bar, but still being able to scroll
I think that will solve your problem
it solved mine
var myElem=function(event){
return $(event.toElement).closest('.slimScrollDiv')
}
$(document).mouseover(function(e){
window.isOnSub=myElem(e).length>0
})
$(document).on('mousewheel',function(e){
if(window.isOnSub){
console.log(e.originalEvent.wheelDelta);
if( myElem(e).prop('scrollHeight')-myElem(e).scrollTop()<=myElem(e).height()&&(e.originalEvent.wheelDelta<0)){
e.preventDefault()
}
}
})
replace '.slimScrollDiv' with the element selector you want to
prevent parent scroll while your mouse is on it
http://jsbin.com/cutube/1/edit?html,js,output
I have been trying to create a test case for a webapplication we are making and I am quite close to a solution, but the final part has me stumped...
We need to be able to drag divs around (no problem there), but some of the divs need to be locked in place (again, no problem).
The problem arises when a draggable div is stuck underneath a non draggable div. I have fixed this somewhat, but it only works if there is only ONE non draggable div on top of the draggable one.The moment it overlaps with another non draggable div, it won't work. Which is weird, as I am correctly accessing the draggable div.
my HTML:
<body>
<div id="div1" class="draggable"></div>
<div id="div2" class="locked"></div>
<div id="div3" class="locked"></div>
</body>
And here is my javascript:
<script>
$(document).ready(function () {
$(document).mousemove(function (e) {
window.mouseXPos = e.pageX;
window.mouseYPos = e.pageY;
});
});
$(function () {
$(".draggable").draggable();
});
$('.locked').ready(function () {
$('.locked').mousedown(function (e) {
var layer = e.target;
$("#data").html($("#data").html() + " " + layer.id);
$(layer).addClass("hide");
var lowerLayer = document.elementFromPoint(window.mouseXPos, window.mouseYPos);
if ($(lowerLayer).hasClass("locked")) {
$(lowerLayer).mousedown();
}
else if ($(lowerLayer).hasClass("draggable")) {
$("#data").html($("#data").html() + " " + lowerLayer.id);
$(lowerLayer).trigger(e);
}
$(layer).removeClass("hide");
});
});
</script>
Okay, so the idea is this, I use jquery to set everything with the class "draggable" to be able to be dragged. The body gets a mousemove event to store the current mouse position. While all the elements with the "locked" class, will fire a mousedown event. In this event, I hide the element I am clicking on by adding a class called "hide" (which contains only a display: none).
At this point, the element underneath the clicked element is visible. Using the elementFromPoint combined with my stored mouse positions, I grab the lower element.
By then checking if it is "locked" or "draggable" I can determine what this element should do. If it's locked, I force it to execute a mousedown event. If it's draggable, I use the .trigger(e) to start dragging.
I then remove the "hide" class again, so that the element does not stay hidden.
Using the div called data, I can see that the function does indeed reach the draggable div. However, if both locked divs are on top of it, it will not start dragging. If there is only one locked div on top, I can then start dragging the draggable div.
In my opinion, this should work without any problems. I mean, it works with only 1 overlapping div, and even with 2 (or more), I am still triggering the code in the else if statement.
What am I missing/overlooking?
-Ferdy
I rewrote your script slightly differently and it seems to work. The problem might be that you're using e.target, but i'm not really sure. Here's what i did:
$(".draggable").draggable();
$(".locked").mousedown(function(e) {
var layer = $(this);
layer.addClass("hide");
var lowerLayer = $(document.elementFromPoint(e.pageX, e.pageY));
if (lowerLayer.hasClass("draggable") || lowerLayer.hasClass("locked"))
lowerLayer.trigger(e);
layer.removeClass("hide");
});
http://jsfiddle.net/AXycq/
I have a <div> on my web page that I would like to have an opacity of 1 while you're hovering over it, but when the mouse is not hovering over it I would like it to fade to an opacity of 0.3. My problem is that currently when I hover over the <div> it starts fading in and out several times (rather than just once). I'm not sure if this is why, but I suspect it's because it detects the mouse rolling over the multiple <div>s that are within the one that I set to fade out.
Here is a very simplified segment of my web page to illustrate what I have so far:
<div id="div1">
<div id="div2" onmouseover="fadeElementTo('div1', 500, 1)" onmouseout="fadeElementTo('div1', 500, 0.3)">
<div id="div3">
<div id="div4">
</div>
</div>
<button id="myButton" onclick="doACoolAnimation()" ></button>
</div>
</div>
My fadeElementTo() function is pretty simple:
function fadeElementTo(eid, speed, opacity, callback) {
$("#" + eid).fadeTo(speed, opacity, callback);
}
In case it's relevant, I also have a button that animates the same div by simply moving it left or right when the button is clicked.
function doACoolAnimation() {
var hiddenState = GLOBAL_VAR.hiddenState;
// If the <div> is already hidden, make it visible
if (hiddenState == null || hiddenState == 1) {
GLOBAL_VAR.hiddenState = 0;
$("#div1").animate({
left: "0px"
}, 1500);
}
// Otherwise, hide it
else {
GLOBAL_VAR.hiddenState = 1;
$("#div1").animate({
left: "-800px"
}, 1500);
}
}
Any ideas what might be causing my bug? And better yet, what can I do to fix it?
Try onmouseenter instead of onmouseover and use jQuery to attach/bind those events rather than the attributes so it works the same across all browsers.
$('#outer').mouseenter(function() {
$('#log').append('<div>Handler for .mouseenter() called.</div>');
});
see here
Use mouseenter event to stop event bubbling, and stop method to make sure you clear unfinished animations on that element.
$('#div2').mouseenter(function(){
$('#div1').stop().fadeTo(500,1);
});
It detects the events multiple times. For example, if you want to change the size, going on and off fast changes the size even when the mouse is not on the div. The code needs to exit the program when the mouse is not on the div. To do that, you might include the code in something that kills the code when the mouse is not on top of the div so that the queued fades/animations do not run.
Edit:
Try looking at the JQuery documentation to see if there is anything that you can use.
You might able to use these:
I have a page layout with an inner <div id="content"> element which contains the important stuff on the page. The important part about the design is:
#content {
height: 300px;
width: 500px;
overflow: scroll;
}
Now when the containing text is larger than 300px, I need to be able to scroll it. Is it possible to scroll the <div>, even when the mouse is not hovering the element (arrow keys should also work)?
Note that I don’t want to disable the ‘global’ scrolling: There should be two scrollbars on the page, the global scrollbar and the scrollbar for the <div>.
The only thing that changes is that the inner <div> should always scroll unless it can’t be moved anymore (in which case the page should start scrolling).
Is this possible to achieve somehow?
Edit
I think the problem was a bit confusing, so I’ll append a sequence of how I would like it to work. (Khez already supplied a proof-of-concept.)
The first image is how the page looks when opened.
Now, the mouse sits in the indicated position and scrolls and what should happen is that
First the inner div scrolls its content (Fig. 2)
The inner div has finished scrolling (Fig. 3)
The body element scrolls so that the div itself gets moved. (Fig. 4)
Hope it is a bit clearer now.
(Image thanks to gomockingbird.com)
I don't think that is possible to achieve without scripting it, which could be messy, considering the numerous events which scroll an element (click, scrollwheel, down arrow, space bar).
An option could be using the jQuery scroll plugin. I know it has the availability to create scrollbars on an div. The only thing you need to add yourself is the logic to catch the events when keyboard buttons are pressed. Just check out the keycodes for the arrow keys and make the div scroll down.
The plugin can be found here.
You can use it like this;
<script type="text/javascript">
// append scrollbar to all DOM nodes with class css-scrollbar
$(function(){
$('.css-scrollbar').scrollbar();
})
</script>
here is a solution that might work: (fiddle: http://jsfiddle.net/maniator/9sb2a/)
var last_scroll = -1;
$(window).scroll(function(e){
if($('#content').scrollTop());
var scroll = $('#view').data('scroll');
if(scroll == undefined){
$('#content').data('scroll', 5);
scroll = $('#content').data('scroll');
}
else {
$('#content').data('scroll', scroll + 5);
scroll = $('#view').data('scroll');
}
/*
console.log({
'window scroll':$('window').scrollTop(),
'scroll var': scroll,
'view scroll':$('#view').scrollTop(),
'view height':$('#view').height(),
'ls': last_scroll
});
//*/
if(last_scroll != $('#content').scrollTop()){ //check for new scroll
last_scroll = $('#content').scrollTop()
$('#content').scrollTop($('#content').scrollTop() + scroll);
$(this).scrollTop(0);
//console.log(e, 'scrolling');
}
})
It is a bit buggy but it is a start :-)
The only way I believe you can achieve this is through the use of frames.
Frames - W3Schools Reference
If you just want to have a fixed positioned "div" and scroll only it, maybe you could use a trick like:
http://jsfiddle.net/3cpvT/
Scrolling with mouse wheel and all kinds of keys works as expected. Only thing is that the scrollbar is on the document body only.
I found a solution... Not perfect... http://jsfiddle.net/fGjUD/6/.
CSS:
body.noscroll {
position: fixed;
overflow-y: scroll;
width: 100%;
}
JS (jQuery):
if ($("body").height() > $(window).height()) {
var top;
$('#scrolldiv').mouseenter(function() {
top = $(window).scrollTop();
$('body').addClass('noscroll').css({top: -top + 'px'});
}).mouseleave(function() {
$('body').removeClass('noscroll');
$(window).scrollTop(top);
});
}
The text wrapping problem can be solved putting the whole content in fixed-width div. There is another bug for IE browser. If the page has center-aligned backgrond, it will move left-right on mouseenter on #scrolldiv