Experiencing some strangeness with jQuery mouse events.
Check the jsFiddle at: https://jsfiddle.net/Lb8r3907/1/
What I am trying to achieve is an inner element that when the mouse is over it, the pointer element will follow your cursor and when you mouse out of the inner element the pointer is then hidden.
I have an outer element that fills the screen and the mouse enter / leave of this element shows and hides the pointer element that will follow your cursor.
$(function() {
$('.outer').on('mouseenter', function(){
console.log('MOUSE OVER OUTER!!');
if($('.pointer').is(':visible')){
$('.pointer').fadeOut(50);
}
});
//
$('.outer').on('mouseleave', function(){
console.log('MOUSE OUT OF OUTER!!!');
if(!$('.pointer').is(':visible')){
$('.pointer').fadeIn(50);
}
});
//
$('.inner').on('mousemove', function(e){
var mX = e.pageX-$('.inner').offset().left,
mY = e.pageY-$('.inner').offset().top;
$('.pointer').css({"top": mY+"px", "left": mX+"px"});
});
});
.outer { position:absolute; display:block; z-index:0; top:0px; left:0px; width:100%; height:100%; background-color:rgba(255,0,0,0.5); }
.inner { position:absolute; display:block; z-index:1; top:50%; left:50%; width:50%; height:25%; margin-top:-12.5%; margin-left:-25%; background-color:#fff; }
.inner .pointer { display:block; position:absolute; top:50%; left:50%; width:50px; height:50px; background-color:blue; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<body>
<div class="outer"></div>
<div class="inner">
<div class="pointer" style="display:none;"></div>
</div>
</body>
What's really odd is that when the mousemove event is activated it seems to delay the firing of the mouseleave event.
I also notice that the delay really only seems to occurr when you mouse out on the right or bottom edge of the inner element.
Can anyone offer any insights as to why this is occurring??
I would really like to know how to address this issue or if it is a bug in the browser / jQuery.
Your "Pointer" is consuming some of the events intended for the "outer" element.
Try some spacing between the cursor and the pointer. The space should help keep the pointer away when you mouse out to the outer, so that the outer gets its events as intended
$('.pointer').css({"top": mY+2+"px", "left": mX+2+"px"});
This change Should work better.
I would have tackled this problem differently. Instead, I would use the boundary box of the .inner and add the .pointer if the cursor was inside. This way you only need one mouse listener and the code is easier to understand (in my opinon):
html:
<div class="container">
<div class="outer"></div>
<div class="inner">
<div class="pointer" style="display:none;"></div>
</div>
</div>
CSS (no change)
JS:
$(function() {
var rect = $('.inner')[0].getBoundingClientRect();
$('.container').on('mousemove', function(e) {
var mX = e.pageX;
var mY = e.pageY;
// Is the cursor inside the boundaries?
if(mX > rect.x && mX < rect.x + rect.width && mY > rect.y && mY < rect.y + rect.height ) {
if(!$('.pointer').is(':visible')) { // This can be optimized as well
$('.pointer').show();
}
$('.pointer').css({"top": mY - rect.y, "left": mX - rect.x});
} else {
$('.pointer').hide(); // This can be optimized as well
}
});
});
You can add CSS pointer-events:none; to .pointer. pointer-events is supported in all major browsers
This CSS property, when set to "none" allows elements to not receive hover/click events, instead the event will occur on anything behind it.
https://caniuse.com/pointer-events
Related
I'm using CSS grid for a timeline. The grid is yielding about 1300 divs, which is really bad for performance.
I need to have each of these cells to be clickable and show a different color on hover. I cannot find any way to style "empty" nodes or interact with them without rendering all of those 1300 divs.
What can I do?
as Mike 'Pomax' Kamermans
suggested, the best way would be detect mouse click and add item dynamically. You can customize the width and height of items by assigning values to item_width and item_height.
var item_width=40;
var item_height=40;
var added_items=[];
$(function(){
$('.grid').on('click', function(e){
var x = e.pageX - $(this).offset().left;
var y = e.pageY - $(this).offset().top;
var item=$('<div class="item"></div>');
var left=Math.floor(x/item_width)*item_width;
var top=Math.floor(y/item_height)*item_height;
var position={ 'left':left, 'top':top };
var index=added_items.findIndex(p => p.left == position.left && p.top == position.top);
if(index<0){
added_items.push(position);
item.css('left', left);
item.css('top', top);
item.css('background', "#"+((1<<24)*Math.random()|0).toString(16))
item.appendTo($('.grid'));
}
});
});
.grid {
width:400px;
height:400px;
border:1px solid red;
position:relative;
margin:10px;
}
.item {
width:40px;
height:40px;
position:absolute;
background:red;
}
.item:hover {
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="grid"> </div>
You can add your columns classes a eventHandler and same classes act same hover or click..
Also 1300 divs are really decreace your page's performance. For that you can research for infinite scroll or like that methods.
https://demos.telerik.com/kendo-ui/grid/endless-scrolling-local
I am trying to make a div .description that appears where the mouse is as it hovers over an element.
So far, I have this code for making the div appear in a fixed location:
$('.tooltip').mouseover(function(e) {
// var to link tooltips and hand to description
var target = $(this).attr('data-show');
// hide current description if showing
if ($('.description').is(':visible')) {
$('.description').fadeOut(0);
};
e.stopPropagation();
$('#' + target).fadeIn(400);
});
$('body').mouseover(function(){
$('.description').fadeOut(0);
});
It works fine, but instead of just have .description appear, I need it to appear where the mouse hovers.
I tried adding this function:
function divPosition (event){
// pass mouse position to description div css
$(".description").css({top: event.clientY, left: event.clientX- 200});
};
but it didn't work. I also tried adding the line within the function but i'm not sure how to pass the event argument.
Please see my JSfiddle: https://jsfiddle.net/bns4zp1q/2/
You can listen to the mousemove event, and use your divPosition function.
https://jsfiddle.net/agbuLop1/
$('.tooltip').mousemove(divPosition);
function divPosition (event){
// pass mouse position to description div css
$(".description").css({top: event.clientY, left: event.clientX- 200});
};
Does the description really have to follow the mouse movement or just appear at hovered circle? Here is a quick and dirty example without javascript:
https://jsfiddle.net/k9jpqom2/1/
(new) HTML:
<div class="tooltip-container">
<div class="image" id="machine1">
<img src="http://axevilw.sellamachine.com/EnhFiles/advert/182/Machine.jpg/Machine.jpg">
<div class="tooltip" id="tooltip1" data-show="description1">1
<div class="description-container">
<div class="description" id="description1">
LED Strip Lighting
</div>
</div>
</div>
</div>
</div>
(additional) CSS
.description-container {
width:1px;height:1px;position:relative;
}
#tooltip1:hover .description {
display:block;
width:10em;
position:absolute;
left:-4em;
height:auto;
}
.tooltip{
width:5%;
height: 6%;
}
If you don't want to edit the HTML you can try something like this:
https://jsfiddle.net/g2p5g2r4/2/
.tooltip .description-container {
width:1px;
height:1px;
}
#tooltip1:hover + .description-container #description1 {
display:block;
width:10em;
position:absolute;
height:auto;
top: 36%;
left: 25%;
}
#tooltip1 {
top: 31%;
}
You will have to fine tune the position of #description1, #description2, etc...
I have a scrollable div container fits multiple "pages" (or div's) inside of it's container.
My goal is to, at any given moment, figure out where inside my red container does it reach the top of my scrollable container. So it can be a constant on scroll event, or a button that triggers this task.
So for example. If I have a absolute div element inside one of my red boxes at top:50px. And if I scroll to where that div element reaches the top of my scrollable container. The trigger should say that I am at 50px of my red container.
I'm having a hard time grasping how to accomplish this but I've tried things like:
$("#pageContent").scroll(function(e) {
console.log($(this).scrollTop());
});
But it doesn't take into account the separate pages and I don't believe it it completely accurate depending on the scale. Any guidance or help would be greatly appreciated.
Here is my code and a jsfiddle to better support my question.
Note: If necessary, I use scrollspy in my project so I could target which red container needs to be checked.
HTML
<div id="pageContent" class="slide" style="background-color: rgb(241, 242, 247); height: 465px;">
<div id="formBox" style="height: 9248.627450980393px;">
<div class="trimSpace" style="width: 1408px; height: 9248.627450980393px;">
<div id="formScale" style="width: 816px; -webkit-transform: scale(1.7254901960784315); display: block;">
<form action="#" id="XaoQjmc0L51z_form" autocomplete="off">
<div class="formContainer" style="width:816px;height:1056px" id="xzOwqphM4GGR_1">
<div class="formContent">
<div class="formBackground">
<div style="position:absolute;top:50px;left:100px;width:450px;height:25px;background-color:#fff;color:#000;">When this reaches the top, the "trigger" should say 50px"</div>
</div>
</div>
</div>
<div class="formContainer" style="width:816px;height:1056px" id="xzOwqphM4GGR_2">
<div class="formContent">
<div class="formBackground"><div style="position:absolute;top:50px;left:100px;width:450px;height:25px;background-color:#fff;color:#000;">This should still say 50px</div></div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
CSS
#pageContent {
position:relative;
padding-bottom:20px;
background-color:#fff;
z-index:2;
overflow:auto;
-webkit-overflow-scrolling: touch;
-webkit-transform: translate(0, 0);
-moz-transform: translate(0, 0);
-ms-transform: translate(0, 0);
transform: translate(0, 0);
}
#formBox {
display: block;
position: relative;
height: 100%;
padding: 15px;
}
.trimSpace {
display: block;
position: relative;
margin: 0 auto;
padding: 0;
height: 100%;
overflow: hidden;
}
#formScale::after {
display: block;
content:'';
padding-bottom:5px;
}
#formScale {
position:relative;
width:816px;
margin:0;
-webkit-transform-origin: 0 0;
-moz-transform-origin: 0 0;
transform-origin: 0 0;
-ms-transform-origin: 0 0;
}
.formContainer {
position:relative;
margin : 0 auto 15px auto;
padding:0;
}
.formContent {
position:absolute;
width:100%;
height:100%;
}
.formBackground {
width:100%;
height:100%;
background-color:red;
}
JS
var PAGEWIDTH = 816;
$(window).resize(function (e) {
zoomProject();
resize();
});
function resize() {
$("#pageContent").css('height', window.innerHeight - 45 + 'px');
}
function zoomProject() {
var maxWidth = $("#formBox").width(),
percent = maxWidth / PAGEWIDTH;
$("#formScale").css({
'transform': 'scale(' + percent + ')',
'-moz-transform': 'scale(' + percent + ')',
'-webkit-transform': 'scale(' + percent + ')',
'-ms-transform': 'scale(' + percent + ')'
});
$(".trimSpace").css('width', (PAGEWIDTH * percent) + 'px');
$("#formBox, .trimSpace").css('height', ($("#formScale").height() * percent) + 'px');
}
zoomProject();
resize();
EDIT:
I don't think I am conveying a good job at relaying what I want to accomplish.
At the moment there are two .formContainer's. When I scroll #pageContainer, the .formContainer divs move up through #pageContainer.
So what I want to accomplish is, when a user clicks the "ME" button or #click (as shown in the fiddle below), I'd like to know where in that particular .formContainer, is it touching the top of #pageContainer.
I do use scroll spy in my real world application so I know which .formContainer is closest to the top. So if you just want to target one .formContainer, that is fine.
I used these white div elements as an example. If I am scrolling #pageContainer, and that white div element is at the top of screen as I am scrolling and I click on "ME", the on click trigger should alert to me that .formContainer is touching the top of #pageContainer at 50px from the top. If, the the red container is just touching the top of #pageContainer, it should say it is 0px from the top.
I hope that helps clear up some misconception.
Here is an updated jsfiddle that shows the kind of action that I want to happen.
I am giving this a stab because I find these things interesting. It might just be a starting point since I have a headache today and am not thinking straight. I'd be willing to bet it can be cleaned up and simplified some.
I also might be over-complicating the approach I took, getting the first visible form, and the positioning. I didn't use the getBoundingClientRect function either.
Instead, I approached it trying to account for padding and margin, using a loop through parent objects up to the pageContent to get the offset relative to that element. Because the form is nested a couple levels deep inside the pageContent element you can't use position(). You also can't use offset() since that changes with scroll. The loop approach allowed me to factor the top margin/padding in. I haven't looked at the other solutions proposed fully so there might be a shorter way to accomplish this.
Keeping in mind that the scale will affect the ACTUAL location of the child elements, you have to divide by your scale percentage when getting the actual location. To do that I moved the scalePercentage to a global var so it was usable by the zoom function and the click.
Here's the core of what I did. The actual fiddle has more logging and junk:
var visForm = getVisibleForm();
var formTop = visForm.position().top;
var parents = visForm.parentsUntil('#pageContent');
var truOffset = 0;
parents.each(function() {
truOffset -= $(this).position().top;
});
// actual location of form relative to pageContent visible pane
var formLoc = truOffset - formTop;
var scaledLoc = formLoc / scalePercent;
Updated Fiddle (forgot to account for scale in get func): http://jsfiddle.net/e6vpq9c8/5/
If I understand your question correctly, what you want is to catch when certain descendant elements reach the top of the outer container, and then determine the position of the visible "page" (div with class formContainer) relative to the top.
If so, the first task is to mark the specific elements that could trigger this:
<div class='triggerElement' style="position:absolute;top:50px;left:100px;width:450px;height:25px;background-color:#fff;color:#000;">When this reaches the top, the "trigger" should say 50px"</div>
Then the code:
// arbitrary horizontal offset - customize for where your trigger elements are placed horizontally
var X_OFFSET = 100;
// determine once, at page load, where outer container is on the page
var outerContainerRect;
$(document).ready(function() {
outerContainerRect = $("#pageContent").get(0).getBoundingClientRect();
});
// when outer container is scrolled
$("#pageContent").scroll(function(e) {
// determine which element is at the top
var topElement = $(document.elementFromPoint(outerContainerRect.left+X_OFFSET, outerContainerRect.top));
/*
// if a trigger element
if (topElement.hasClass("triggerElement")) {
// get trigger element's position relative to page
console.log(topElement.position().top);
}
*/
var page = topElement.closest(".formContainer");
if (page.length > 0) {
console.log(-page.get(0).getBoundingClientRect().top);
}
});
EDIT: Changed code to check formContainer elements rather than descendant elements, as per your comment.
http://jsfiddle.net/j6ybgf58/23/
EDIT #2: A simpler approach, given that you know which formContainer to target:
$("#pageContent").scroll(function(e) {
console.log($(this).scrollTop() - $("#xzOwqphM4GGR_1").position().top);
});
http://jsfiddle.net/rL4Ly3yy/5/
However, it still gives different results based on the size of the window. This seems unavoidable - the zoomProject and resize functions are explicitly resizing the content, so you would have to apply the inverse transforms to the number you get from this code if you want it in the original coordinate system.
I do not fully understand what it is that you are needing, but if i am correct this should do the trick
$("#pageContent").scroll(function(e) {
// If more then 50 pixels from the top has been scrolled
// * if you want it to only happen at 50px, just execute this once by removing the scroll listener on pageContent
if((this.scrollHeight - this.scrollTop) < (this.scrollHeight - 50)) {
alert('it is');
}
});
ScrollHeight is the full height of the object including scrollable pixels.
ScrollTop is the amount of pixels scrolled from the top.
You can use waypoints to detect the position of divs based on where you're scrolling.
Here is a link to their official website's example: http://imakewebthings.com/waypoints/shortcuts/inview/
I am trying to run some script when div reaches to a certain point when it's scrolled. There is a fixed navigation and when the user scrolls the window it suppose change the nav name once it reaches close to the nav. I am using the $(window).scroll function but it only checking the div position once and not updating the value. How to make scroll check the window size every 5-10 px move so that it doesn't take too much memory/processing.
The code is set up at: http://jsfiddle.net/rexonms/hyMxq/
HTML
<div id="nav"> NAVIGATION
<div class="message">div name</div>
</div>
<div id="main">
<div id="a">Div A</div>
<div id ="b"> Div B</div>
<div id ="c"> Div C</div>
</div>
CSS
#nav {
height: 50px;
background-color: #999;
position:fixed;
top:0;
width:100%;
}
#main{
margin-top:55px;
}
#a, #b, #c {
height:300px;
background-color:#ddd;
margin-bottom:2px;
}
SCRIPT
$(window).scroll(function() {
var b = $('#b').position();
$('.message').text(b.top);
if (b.top == 55) {
$('.message').text("Div B");
}
});
Try this jsFiddle example
$(window).scroll(function() {
var scrollTop = $(window).scrollTop(),
divOffset = $('#b').offset().top,
dist = (divOffset - scrollTop);
$('.message').text(dist);
if (b.top == 55) {
$('.message').text("Div B");
}
});
Your original code was only checking the position of the div relative to the top of the document which never changes. You need to calculate in the amount of scroll the window has incurred and calculate accordingly.
Also note the difference beyween jQuery's .position() and .offset() methods. The .position() method allows us to retrieve the current position of an element relative to the offset parent. Contrast this with .offset(), which retrieves the current position relative to the document.
I'm trying to develop a slide gallery with image tooltips according to this design:
What I need to develop is a slider controlled by two buttons, each time a button is pressed the slider's content must move a width of the slider or the width of the content left on that side, whichever is smaller. Upon mouse entering an image inside the slider the full-size version must be displayed as a tooltip.
Here's a fiddle of my solution so far, the problem I'm having is that images that don't fully fit into view plus the hidden area to the left get moved to a new line. You can see the problem by clicking the
"Show content size" button, the width of the content element will be equal to the width of the container element + content element's margin-left.
Bonus points if you can suggest an algorithm for moving the content to the right, I've got left figured out to a T (or so I think, anyway), but right is going to take a little more work (it doesn't check whether the end of the content has been reached). Update: It seems I can't implement proper movement to the right until the other issue is resolved, here's the algorithm I came up with, I can't measure "left to display" if I can't measure the actual width of the content element.
I created something you might like:
gallery demo
The gallery does not scroll the full gallery width by default (you can change that) cause some initially cut-off images at the right side, after a 'full' slide would result cut-off again, just on the other side of our gallery. You have for that cause the beKind variable. Adjust it as you like.
It hides the buttons if there's not enough content to make the gallery usable.
The gallery calculates the remaining space to scroll.
Once the slider end reached - the left/right buttons make the gallery jump to the beginning/end, so that are always usable. (Seems kinda weird to have a button... but that does nothing right? ;) )
The Tooltip has a hover-intent built in, to not piss off our users if they unintentionally hovered our gallery: (the tooltip fades in if the hover is registered for more that 120ms. Fair timing. I like it.)
As pointed out in your comment now the tooltip will not go off the screen.
jQ:
// Slide Kind Gallery - by roXon // non plugin v. // CC 2012.
$(window).load(function(){
var galW = $('#gallery').outerWidth(true),
beKind = 120, // px substracted to the full animation to allow some images to be fully visible - if initially partly visible.
sumW = 0;
$('#slider img').each(function(){
sumW += $(this).outerWidth(true);
});
$('#slider').width(sumW);
if(sumW <= galW){ $('.gal_btn').remove(); }
function anim(dir){
var sliderPos = Math.abs($('#slider').position().left),
rem = dir ==='-=' ? rem = sumW-(sliderPos+galW) : rem = sliderPos,
movePx = rem<=galW ? movePx = rem : movePx = galW-beKind;
if( movePx <= 10){
movePx = dir==='-=' ? movePx=rem : movePx = galW-sumW;
dir = '';
}
$('#slider').stop(1).animate({left: dir+''+movePx },1000);
}
$('.gal_btn').on('click', function(){
var doit = $(this).hasClass('gal_left') ? anim('+=') : anim('-=');
});
});
And the tooltip script:
// Addon // Tooltip script
var $tt = $('#tooltip');
var ttW2 = $tt.outerWidth(true)/2;
var winW = 0;
function getWW(){ winW = $(window).width(); }
getWW();
$(window).on('resize', getWW);
$('#slider img').on('mousemove',function(e){
var m = {x: e.pageX, y: e.pageY};
if( m.x <= ttW2 ){
m.x = ttW2;
}else if( m.x >= (winW-ttW2) ){
m.x = winW-ttW2;
}
$tt.css({left: m.x-ttW2, top: m.y+10});
}).hover(function(){
$clon = $(this).clone();
var t = setTimeout(function() {
$tt.empty().append( $clon ).stop().fadeTo(300,1);
},120);
$(this).data('timeout', t);
},function(){
$tt.stop().fadeTo(300,0,function(){
$(this).hide();
});
clearTimeout($(this).data('timeout'));
});
HTML
(Place the #tooltip div after the body tag)
<div id="tooltip"></div>
<div id="gallery_container">
<div id="gallery">
<div id="slider">
<img src="" alt="" />
<img src="" alt="" />
</div>
</div>
<div class="gal_left gal_btn">◀</div>
<div class="gal_right gal_btn">▶</div>
</div>
CSS:
/*GALLERY*/
#gallery_container{
position:relative;
margin:0 auto;
width:600px;
padding:0 30px; /*for the buttons */
background:#eee;
border-radius:5px;
box-shadow: 0 2px 3px #888;
}
#gallery{
position:relative;
height:100px;
width:600px;
overflow:hidden;
}
#slider{
position:absolute;
left:0px;
height:100px;
}
#slider img{
height:100.999%; /* fixes some MOZ image resize inconsistencies */
float:left;
cursor:pointer;
border-right:3px solid transparent; /* instead of margin that could leat to some wrong widths calculations. */
}
.gal_btn{
position:absolute;
top:0px;
width:30px; /*the container padding */
height:40px;
padding:30px 0;
text-align:center;
font-size:30px;
cursor:pointer;
}
.gal_left{left:0px;}
.gal_right{right:0px;}
/* end GALLERY */
/* TOOLTIP ADDON */
#tooltip{
position:absolute;
z-index:100;
width:300px;
padding:10px;
background:#fff;
background: rgba(255,255,255,0.3);
box-shadow:0px 3px 6px -2px #111;
display:none;
}
#tooltip *{
width:100%;
vertical-align:middle;
}
/* end TOOLTIP ADDON */
Hope you'll like it, and you learned some useful UI design tricks.
By the way, if you want to populate your ALT attributes (Search engines like it!) you can also grab that text and make it appear inside the tooltip like here!:
demo with text inside the tooltip
Happy coding.
I don't know if I understand correctly your problem. If you set a width wide enough to .scroll-content div, images wouldn't go to the "next line". So a solution would be to set a width with css. If not, you could use jquery to determine the total width of all the images and give it to the .scroll-content div. Calculate total width of Children with jQuery