Mouse-Following Tooltip gets Farther Away the Smaller the Window - javascript

I've just revamped my tooltip code due to issues with the position altering depending on the size of it's parent (mostly due to using offsetX/Y instead of pageX/Y, but page was being weird, too). So I decided to just have one tooltip for each of my site's pages, parented to the main div, and just feed it different text depending on what the mouse is hovering over (I'll be dealing with the visibility part later).
And it's worked quite well so far, but the only issue is that, the smaller I make my window, the farther the tooltip is from my mouse, until it's not even in view anymore.
Here's the JavaScript coding I've done for it.
var body = document.getElementsByClassName("test");
var tooltip = document.getElementById("tooltip");
body[0].addEventListener("mousemove", tooltipMove)
function tooltipMove(event) {
var x = event.pageX;
var y = event.pageY;
tooltip.style.top = (y + -900) + "px";
tooltip.style.left = (x + -875) + "px";
}
The CSS coding for the tooltip:
.tooltip {
visibility: hidden;
width: 170px;
background-color: white;
background-image: url("images/tooltipbackground.png");
color: black;
text-align: center;
border-style: groove;
border-color: #f4bb4c #ffd966 #ffd966 #f4bb4c;
border-radius: 2px;
padding: 5px 5px;
position: absolute;
z-index: 1;
}
.notfound:hover .tooltip {
visibility: visible;
}
And the HTML:
<div class="test" style="top: 70px; position: relative; height: 100%; width: 100%;">
<h1>TEST</h1>
<img src="images/pagenotfound.png">
</div>
<div style="width: 1px; height: 1px; position: relative;">
<span class="tooltip" id="tooltip">testing</span>
</div>
I should mention the body's (which has the "notfound" class) height is 900px, and it's width 600px, in case that's one of the problems.
The 1 pixel div is just what I'm using to "host" the tooltip, not sure if it's causing any problems as well. I inspected the page in order to see it, and it never seemed to slide around with the window size.
Any sort of help would be greatly appreciated. I've tried to switch it from pageX/Y to clientX/Y, but it's the same issue. And using offset causes it's position to shift depending on what I'm hovering over, which is the reason I'm revamping the code in the first place.
I've also tried to change the tooltip's position from absolute to, well, anything else (after resizing it's parent so it doesn't get squashed), but that hasn't helped.
Another thing I should mention is that, for some reason, the shifting doesn't seem to happen in the Y axis, it's only when I squish the window horizontally that the tooltip shifts, at least from what I've noticed.

I had thought changing the tooltip's position to fixed had made it disappear, but I just couldn't see it due to the massive repositioning I had done to it. Once I deleted that it was visible and fine, and better yet, it stays in it's proper position no matter the screen size!
Also note: I had to change pageX/Y to clientX/Y, as using page made the tooltip shift vertically when squished.
<div style="height: 1px; width: 1px; position: relative;">
<span class="tooltip" id="tooltip" style="position: fixed;">Placeholder</span>
</div>
for (i = 0; i < tip.length; i++) {
tip[i].addEventListener("mousemove", tooltipMove)
tip[i].addEventListener("mouseleave", defaultVis)
}
function tooltipMove(event) {
var x = event.clientX;
var y = event.clientY;
tooltip.style.visibility = "visible";
tooltip.style.top = (y + -50) + "px";
tooltip.style.left = (x + -200) + "px";
}
function defaultVis() {
tooltip.style.visibility = "hidden";
}

Related

(Electron) How to get the position of a draggable element in a smaller div and translate that to a large div

I'm creating an Electron program that loops a video comprising of adverts. The video spans the entire window. Let's call this window A.
There is a second, smaller window used to configure aspects of window A. Let's call this window B.
The purpose of window B is to control aspects of window A in real time, such as overlaying a message over the video or hiding the video controls. Here's an image to explain further:
Seen in the photo above, there is a further window open displaying a smaller video. Let's call this window C. The blue rectangle can be dragged around and represents a message that will be display over the video on window A.
If I save the coordinates of that rectangle and create an element on the window A video, the coordinates don't match up due to the difference in window sizes. What I can't figure out is how to take the coordinates in window C and apply them to window A correctly. I've tried working with clientWidth/clientHeight and messing with numerous CodePen's to no avail.
Any help would be greatly appreciated.
Each pixel in the smaller div equals X pixels in the bigger div. That's the ratio to multiply with. Let's assume mouse movement inside smaller div is like dragging and affecting the bigger div.
var small = document.querySelector(".small");
var big = document.querySelector(".big");
var rectangle = document.querySelector(".rectangle");
var ratioX = big.clientWidth / small.clientWidth
var ratioY = big.clientHeight / small.clientHeight
small.addEventListener("mousemove", function(ev) {
var mx = ev.offsetX
var my = ev.offsetY
rectangle.style.left = mx * ratioX + "px"
rectangle.style.top = my * ratioY + "px"
})
.small {
width: 100px;
height: 50px;
border: 1px solid black;
}
.big {
width: 300px;
height: 150px;
border: 1px solid black;
position: relative;
overflow: hidden;
}
.rectangle {
width: 20px;
height: 20px;
background: blue;
position: absolute;
}
Mouse over this:
<div class="small">
</div>
Will move this:
<div class="big">
<div class="rectangle">
</div>
</div>

Alternative to absolute positioning that updates parents height

I am working on a library that draws brackets.
I am calculating the coordinates of each element (matches and lines) using javascript. Each element has position: absolute; and uses left and top to set their x and y coordinates.
Because I am using position: absolute; it is necessary to set the parent to position: relative; so that the children are positioned relative to their parent.
This was working perfectly until I noticed that the parents height was not updating from its children, because position: absolute; elements are taken out of the flow of the document.
I need the parent to have height so that I can place other elements underneath, and give the parent styles such as background-color...
Is there an alternative to absolute positioning that uses x and y coordinates but also keeps them in the flow of the document to allow the parents width and height to adjust automatically?
Or, if that is not possible, is there away using vanilla javascript (no jQuery or other libraries) to find out the width and height of the contents of the parent div. If this is possible I can just set the parent's width and height styles through javascript.
What I've tried so far
I tried to set the children to position: relative; instead of position: absolute; which I believe would work if you only have one child. However, with more than one child, the children are not relative to the parent but are now relative to the previous child which messes things up.
Even though the parent has no height there is still a vertical scrollbar on the page. Using javascript, I tried to get the scrollHeight and height of elements such as document, document.body and window. This did not work because either the result was undefined or the result was incorrect.
Right now my temporary solution is to set body height to 2500px which should be the highest it will ever need to be. The problem with this is that there will always be a scrollbar, most of the time scrolling to nothing.
Code
<div class="BrackChart_wrapper">
<div class="BrackChart_match"> ... </div>
<div class="BrackChart_line"></div>
etc.
</div>
.BrackChart_wrapper {
position: relative;
top: 0px;
left: 0px;
}
.BrackChart_match, .BrackChart_line {
position: absolute;
}
Thank you for the help, much appreciated!
JSFiddle: https://jsfiddle.net/jmjcocq8/1/
Solution: https://jsfiddle.net/jmjcocq8/2/
I'm fairly sure there's no alternative that both lets you position the children absolutely (relative to their parent) and keeps them in the flow such that they contribute to the size of the parent.
But since you're calculating the positions of the elements yourself, and you know how tall they are, you can set the height of the parent based on the lowest child's y plus height.
You haven't shown your code, but for instance:
var forEach = Array.prototype.forEach;
function positionChildren(parent) {
var left = 0;
var top = 0;
forEach.call(parent.children, function(child, index) {
child.style.left = left + "px";
child.style.top = top + "px";
left += 20;
top += 10;
});
var height = top - 10 + 1 + parent.lastElementChild.clientHeight;
console.log("height = " + height);
parent.style.height = height + "px";
}
positionChildren(document.getElementById("parent"));
#parent {
border: 1px solid black;
background-color: #ddd;
position: relative;
}
.child {
position: absolute;
border: 1px solid blue;
padding: 0;
margin: 0;
}
<div id="parent">
<div class="child">Child1</div>
<div class="child">Child2</div>
<div class="child">Child3</div>
</div>

Updating position of fixed element on scroll event on unresponsive page

I have a large page running a lot of javascript which also contains a fixed position floating element. The floating element updates its position based on the user's current scroll position, via a function which fires on the scroll event.
The issue I am having is that due to the size and complexity of the page, there is a delay before the code in the scroll event is executed, and this causes the fixed element to noticeably jump when scrolling quickly or using the mouse wheel.
See jsfiddle: http://jsfiddle.net/jorsgj2b/1/ (The use of setTimeout simulates the delay in executing the function on the real page.)
<div class="header"></div>
<div class="main"></div>
<div class="float"></div>
.header {
width: 100%;
height: 200px;
background-color: #787878;
}
.main {
width: 100%;
height: 1400px;
background-color: lightblue;
}
.float {
position: fixed;
width: 100px;
height: 50px;
top: 233px;
right: 25px;
background-color: red;
z-index: 2;
}
$(function() {
$(window).scroll(function() {
setTimeout(updateFloat, 50);
});
});
var updateFloat = function() {
var mainTop = $('.main').offset().top;
var scrollPos = $(window).scrollTop();
var floatOffset = 25;
var newTop = (scrollPos > mainTop)
? floatOffset + 'px'
: mainTop + floatOffset - scrollPos + 'px';
$('.float').css('top', newTop);;
}
I am at a bit of a loss as to how to resolve this. I have tried updating the margin instead of top position, as well as switching between absolute and fixed positioning. Perhaps there is a way to use css transitions to help, however I haven't managed to get them to work here.
You might add position: sticky to your css to that the stickiness is done by browsers that support it (only Firefox and Safari according to caniuse.com).
You'll always be limited to the fidelity of the scroll events so you may always see a bit of a delay, but you could improve things a little by caching values instead of looking them up each time updateFloat() is called. For example
var mainElement = $('.main');
var windowElement = $(window);
var floatElement = $('.float');
var updateFloat = function() {
var mainTop = mainElement.offset().top;
var scrollPos = windowElement.scrollTop();
var floatOffset = 25;
var newTop = (scrollPos > mainTop)
? floatOffset + 'px'
: mainTop + floatOffset - scrollPos + 'px';
floatElement.css('top', newTop);
}
The problem might also be related to browser trying to re-layout the page because the .float element is in the same render layer as the other divs. You can fix that by adding a style that tells the browser to put the .float element in its own render layer (resulting in much faster rendering due to GPU compositing). The most common trick is to add transform: translateZ(0); to your style, but there's also a proposed style will-change that is supported by several browsers. So you should update your css like this
.float {
will-change: scroll-position;
transform: translateZ(0);
position: fixed;
width: 100px;
height: 50px;
top: 233px;
right: 25px;
background-color: red;
z-index: 2;
}
Note that adding render layers increases memory usage so don't go overboard with it. It also sometimes impacts other features, like anti-aliasing of text.

CSS Dynamic 3D carousel, z-axis positioning

I'm creating a 3D carousel using CSS and javascript. For testing and developing, I've uploaded what I have so far on this page: http://dev.rushfivedesigns.com/
When you get to the page, please hit the "initialize" button to have it transformed into 3D space. By default, it will initialize with 5 panels, and this can be changed using the controls.
The problem I'd like to solve is this: When I increase the number of panels, the distance from the origin increases and so the panels increase in perceptible size (they get blown up). I'd like it if the front panel would always retain the same size, regardless of how many panels there are.
So rather than pushing every panel out by x distance, I want the front panel to stay at a static location in 3D space, and then everything else is pushed around behind it (hope that makes sense).
I've made this using angular, but this could easily be made using plain javascript as well. Here's the relevant code:
HTML
<div id="musicPlayer">
<section class="container">
<div id="carousel">
<figure class="something" ng-repeat="item in items">item {{item.someKey}}</figure>
</div>
</section>
</div>
CSS
.container {
width:300px;
height:300px;
position:relative;
perspective: 1000px;
margin-left: 400px;
margin-top:100px;
}
#carousel {
width: 100%;
height: 100%;
position: absolute;
transform-style: preserve-3d;
}
#carousel figure {
display: block;
position: absolute;
left: 10px;
top: 10px;
border: 2px solid black;
width: 276px;
height: 276px;
}
Javascript
$scope.items = [];
for (var i = 0; i < 5; i++) {
var filler = {
someKey: i
};
$scope.items.push(filler);
};
$scope.initializeCarousel = function () {
var carousel = document.getElementById('carousel');
var numPanels = $scope.items.length;
var theta = 360 / numPanels; // rotation between each panel in 3D space
var radius = Math.round(150 / Math.tan(Math.PI / numPanels)); // how far in Z-axis the panels are pushed out
//rotate panels by theta
for (var i = 0; i < $scope.items.length; i++) {
var panel = carousel.children[i];
var angle = theta * i;
panel.style.transform = 'rotateY(' + angle + 'deg) translateZ(' + radius + 'px)';
};
};
Everytime the "initialize" button is pressed, the $scope.initializeCarousel function is called, using the # of panels chosen.
I have a feeling this may just be related to CSS coding, and not necessarily the javascript, but I'm really not sure. I'm completely new to CSS animating.
Any guidance on this would be great. Thanks S.O.!
My guess is you need to figure out the radius of the "sphere" and move all the panels in the z direction at least that distance forward. The better way to do it would be by moving the camera which is where you are positioned away from the object.

How to user controlled overlap images in JS

I'm desperately searching for solution for my client. I have graphic - something like that:
And I want to be able to take the line with circle in the center and drag it to right or left. And it will be hiding and unhiding my two full images. It's basically two images on the same place, just with another z-index I think.
I think it's possible to do it with JavaScript, but I don't know of any functions or methods for this option.
Here is my solution:
The HTML is pretty simple, just two divs for the images and one for the drag:
<div class="img" id="img1"></div>
<div class="img" id="img2"></div>
<div id="drag"></div>
For the CSS, the important part is to absolute position all the divs and give a background image.
As for the Javascript, with a little help from jQuery, we listen for the mouse events, make some calculations and adjust the CSS of the second image:
$('#drag').on('mousedown', function(e){
var $self = $(this),
dragPos = $self.position().left + $self.width()/2,
imgWidth = $('#img1').width();
$(document).on('mouseup', function(e){
$(document).off('mouseup').off('mousemove');
});
$(document).on('mousemove', function(me){
var mx = me.pageX - e.pageX + dragPos
$self.css({ left: mx });
$('#img2').css({
width: imgWidth - mx,
left: mx,
backgroundPosition: -mx + 'px 0px',
});
});
});
From there, I believe it's pretty easy to customize it and give it a unique look.
Hope this helps!
JsFiddle Demo
Something like this alphamask plugin may do the trick, though I'm not sure how simple it would be for you to implement in the manner of your slider example.
Actually quite simple. The first step is to make it work manually. I'd set it up as follows:
<div class="wrap" id="wrap1">
<div class="img-wrap img1"></div>
<div class="img-wrap img2"></div>
<div>
With CSS as follows:
.wrap {
position: relative;
width: 300px;
height: 300px;
}
.img-wrap {
width: 100%;
height: 100%;
position: absolute;
top: 0px;
left: 0px;
}
.img1 {
z-index: 1;
background: url(bg1.png) no-repeat 0px 0px;
}
.img2 {
z-index: 2;
background: url(bg1.png) no-repeat 0px 0px;
}
Now some JavaScript (with jQuery) to set a position (you can call this when you move a slider over the top later):
function setPosition(percentage){
// get the width of the container
var w = $('#wrap1').width();
// work out the width of left panel
var w1 = Math.floor(w * percentage);
// and the right panel
var w2 = w - w1;
// set the width of the right panel
// move it right by the width of the left panel
// and move the background back by the width of the left panel
$('#wrap1 .img2').css({
width: w2,
left: w1,
backgroundPosition: -w1 + 'px 0px',
});
}
You now just have to decide how to do the dragging. You could even just do it on mouseOver. Easy!

Categories

Resources