I have the following HTML
<div id="chart1">
<div class="tooltip"></div>
</div>
and here is the CSS for the tooltip
.tooltip
{
position: absolute;
border: 1px dashed gray;
background-color: white;
padding: 5px 20px;
min-width: 50px;
min-height: 20px;
text-align: center;
visibility: hidden;
pointer-events: none;
}
and here is my JS
pie.on("mouseenter",function(){
d3.select("#chart1 .tooltip").style("visibility", "visible");
})
.on('mousemove',function(d){
var tooltip = d3.select("#chart1 .tooltip");
show_data(tooltip,d); // custom function to populate tooltip html
tooltip.style("left", (d3.event.pageX - 50) + "px")
.style("top", (d3.event.pageY - 40) + "px");
});
This works perfectly on my local machine, both in Chrome and Explorer Edge.
But when I host all of the files on sharepoint, the tooltip is offset from the mouse pointer. It still follows the same direction as the mouse, but there is a gap in the Y position.
Please see the image below to understand what is going on.
I tried a combination of things, including using window.pageYOffset but that did not work. I am sure sharepoint is adding iframes or sub-html documents behind the scene which makes the mouse position on the SVG element differs from when the file is directly displayed without sharepoint intervention.
What can I do to make the tooltip stick to the mouse position on sharepoint hosted files?
I managed to find a solution to my question.
Instead of populating pure html element with tooltip data, I decided to create an SVG group within my chart.
That worked perfectly across all browsers and even locally
var pie = svg.selectAll(".pie")
.on('mouseenter',show_tooltip)
.on('mousemove',move_tooltip)
.on('mouseout',hide_tooltip );
function create_tooltip() {
tooltip = svg.append("g")
.attr("class","tooltip");
tooltip.append("rect").attr("class","box");
tooltip.append("text").attr("id","division");
tooltip.append("rect").attr("id","legend")
.attr("width","20")
.attr("height","20");
}
function move_tooltip(d) {
var coords = d3.mouse(this);
var x = coords[0] - 30;
var y = coords[1] - 40;
tooltip.attr("transform","translate("+x+","+y+")");
}
function hide_tooltip(d) {
tooltip.transition().attr("opacity","0");
}
function show_tooltip(d) {
tooltip.transition().delay(500).attr("opacity","0.95");
tooltip.select("#division").text(d.data.division);
tooltip.select("#legend").attr("fill",function(){
return color(d.data.division)
});
}
Related
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";
}
I have a d3 implementation which displays hover panels over elements on user mouseover. I have a new requirement to display a summary panel of the same style towards the lower right-hand corner of the svg. Here's the d3 code that I use to display the onmouseover hover panels:
d3.select('UnitedStatesTotalPanel')
.style("top", (d3.event.pageY-10)+"px")
.style("left", (d3.event.pageX+10)+"px");
Is it possible to adapt this code to display a div over the svg in a fixed position? The problem is that the code above is based on the occurence of an event so it seems that this code will need to be adapted or changed to accomodate a one-time display of a div.
You can use offsetTop and offsetLeft to get the position of your SVG, and then set the position of your div accordingly.
The problem is that SVGs don't support offsetTop/offsetLeft, so, you'll have to wrap your SVG in a <div>.
To show the div independent of the mouseover event just append it, as any other regular element.
Check this demo:
var svgPosition = document.getElementById("mySvg");
var div = d3.select("body")
.append("div")
.attr("class", "fixedDiv");
div.html("This is a fixed div<br>based on SVG position")
.style("top", svgPosition.offsetTop + 90 + "px")
.style("left", svgPosition.offsetLeft + 10 + "px");
svg {
background-color: tan;
}
.fixedDiv {
position: absolute;
padding: 4px;
border: 1px solid gray;
border-radius: 4px;
background-color: lightblue;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<div style="float:left; padding:4px;">Lorem ipsum<br>dolor sit amet</div>
<div style="overflow:hidden;" id="mySvg">
<svg ></svg>
</div>
https://jsfiddle.net/m0zwwav4/
html:
<div id="container"></div>
<div id="tooltip">Tooltip!</div>
css:
#container {
width: 500px;
height: 500px;
border: solid 1px red;
}
#tooltip {
position: absolute;
}
js:
var container = document.getElementById('container')
var tooltip = document.getElementById('tooltip')
container.onmousemove = function(event) {
tooltip.style.left = (event.pageX + 20) + 'px'
tooltip.style.top = event.pageY + 'px'
}
When I move cursor inside red box, tooltip seems to be little bit laggy (there is a little delay) - testing in chrome on max os. Is there any trick to make it faster to make it look like moving exactly fast as mouse cursor?
You can do this without JavaScript.
Change the container's cursor to a URL, which is an image containing the tooltip text:
You can do this using a Data URI:
#container {
cursor: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEoAAAAZCAIAAAAZqC9/AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIPSURBVFhH5Zg7csIwEIZxzgKp0kCH6KCCg0BBAQV0KdOFwpTQ5QapcAUdLqFhUgB3cX5Ji2T5IRNjBsb5ZjLj1YqVPz1sT5wgCCrl5QV/jDHHcfb7vWwqE1wP7Ha7yWRSPkPSA+v1ejgclsxQ6wHf90u2hoYewBqWyTCqBx5l6A3wgHOcgUdxESToAYuhvIckWvMz9bkfNAVXD5WsB/6+hm+vVboqhoTVPB8PdHUlqXog0XCB7wDBqi/i/oriYNEVDfekOtqKobajK2fSpgfynMPzvCWmXRLdR/bsBb5yvaW4XPZCHc0VlbWQChU1K6bqdTod2X+z2TQaDWrNgo9TG/sUcfxxTe0vezYvqBEqiihUMKoHK7VW+JQRW4EjW7LwPuU4lx17chkPlx9iSu1Zgy7OgLH77duRuSfeiX4RKmjowQ1rRUEOvG+xn5g7pWNYHb2LEf2fU1b2Bpj7JeW7UzlhuqDWg9tsNsMcyMN2w9vP/ggt+gFrhfRwuuBWr9dleBuHo9psCU9ye7ZosFzNZjN8zEC73ZYt6iJO7MWgDpNupD4ytmfjBSmmcyUx+6iK5tnTJQKuFyennh7AQN3gVVldMNzdFIjqmYRuKVkPZBrG7oaIjBnJW7IJBbWgTY+5K13VHC+/3hOg9Gj946TqAYjhx8/qBrL1Ur9aAN4N6FHQ4/Qx/IP/lJWYUutVKr8O6jUK23d1QgAAAABJRU5ErkJggg==), auto;
}
Fiddle
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.
I have two versions of an image: a desaturated version and a full color version. What I want to accomplish is a hover effect in which mousing over the desaturated image reveals a circle of the color version of the image. It would be sort of like shining a spotlight on the desaturated image to show its color. And then when you move the mouse away, it would fade back to its desaturated state.
I know I could probably use flash, but I'd like to do this with JavaScript and CSS. Ideally this would degrade to just an image if JavaScript is disabled and could be fluid in width (responsive).
border-radius
CSS3 border-radius can be used to create a round div with a background image which serves as the image spotlight. The spotlight can be overlaid on top of the main image, and positioned based on the mouse coordinates. JSFiddle Demo
Although there's no natural way to soften the edges of the spotlight in CSS3 -- which would require support for adding an opacity gradient to arbitrary content -- it can be simulated using a staggered set of elements with increasing radius and decreasing opacity. Updated demo with softened edges
In the updated demo, the size and softness of the the spotlight can be adjusted using the following variables:
var spotlightDiameter = 150; // Base size (not including the soft edge)
var numSpotlightLayers = 6; // More layers = softer edges
var spotlightLayerThickness = 2; // Thinner = the softening is more subtle
Here's a modified demo where the spotlight has noticeable ripples. The thickness of the layers was increased to show more clearly how it works.
Below is a simplified version of the code for the initial version with sharp edges.
HTML
<div class="content">
<div class="spotlight"></div>
</div>
CSS
.content {
position: relative;
width: 640px;
height: 480px;
background: url(desaturated.jpg) no-repeat 0 0;
overflow: hidden;
}
.spotlight {
display: none;
position: absolute;
background: url(overly_saturated.jpg) no-repeat 0 0;
}
jQuery
var spotlightDiameter = 150;
// Update the spotlight position on mousemove
$('.content').on('mousemove', function(e){
var center = {x: e.pageX - this.offsetLeft,
y: e.pageY - this.offsetTop};
var x = center.x - (spotlightDiameter >> 1);
var y = center.y - (spotlightDiameter >> 1);
$('.spotlight').css({left: x + 'px', top: y + 'px',
backgroundPosition: -x + 'px ' + -y + 'px'}).show();
});
// Hide the spotlight on mouseout
$('.content').on('mouseout', function(e){
$('.spotlight').hide();
});
// Initialize the spotlight
$(document).ready(function(){
$('.spotlight').width(spotlightDiameter + 'px')
.height(spotlightDiameter + 'px')
.css({borderRadius: (spotlightDiameter >> 1) + 'px'});
});
Alternative implementations
This could also be implemented using HTML5 Canvas or SVG. Below is a browser-support comparison of the different approaches:
border-radius: Not supported by IE8 or earlier.
HTML5 Canvas: Not supported by IE8 or earlier.
SVG: Not supported by IE8 or earlier, or Android 2.3 or earlier (which is still most of the Android marketshare).
In short, IE8 and earlier is not an option for any of these approaches, and if Android support is needed, that limits the choices to border-radius and HTML5 Canvas. Of course, since this is mouse-based, Android support may not be a factor anyhow.
Use two SVG <image> elements overlaid exactly on top of one another. The bottom is the greyscale image. The top is the color image. Apply a clipPath to the color image, and then adjust the transform on the clipping path to reveal different areas of the upper image.
Simple Demo: http://jsfiddle.net/hZgkz/
SVG:
<svg xmlns="http://www.w3.org/2000/svg" width="200px" height="250px">
<defs>
<image id="im" width="500" height="500"
xlink:href="http://www.nyweamet.org/wp-content/uploads/2011/09/0942a__grandCentralStationExterior.jpg"/>
<clipPath id="clip">
<path id="path" transform="translate(40,60)"
d="M60,0 A30,30 1,0,0 60,120 30,30 1,0,0, 60,0"/>
</clipPath>
</defs>
<use id="clippedImage" xlink:href="#im" clip-path="url(#clip)"/>
</svg>
and the JavaScript that moves the circle around:
var tx = document.querySelector('#path').transform.baseVal.getItem(0);
setInterval(function(){
var ms = (new Date)*1;
tx.matrix.e = Math.sin(ms/812)*150 + 160;
tx.matrix.f = Math.cos(ms/437)*60 + 70;
},50);
All you have to do is track the mouse movement and set the translation to the right spot.
You can use some CSS to achieve the result, if I have understood correctly your request. I've prepared a small fiddle as a demo here: http://jsfiddle.net/sandro_paganotti/k3AmZ/
Here's the code involved:
HTML
<figure data-desaturated></figure>
<figure data-original></figure>
CSS
figure{ width: 550px; height: 360px;
position: absolute; top: 0; left: 0;
margin: 0; padding: 0;
background-position: 50% 50%;
background-repeat: no-repeat;
background-image: url('yourimage.png');
}
figure[data-desaturated]{
/* I've used CSS filters tu simulate desaturation, you can use your already desaturated image */
-webkit-filter: grayscale(0.9);
}
figure[data-original]{
width: 360px;
left: 95px;
border-radius: 180px;
opacity: 0;
transition: opacity 0.4s;
}
figure[data-desaturated]:hover + figure[data-original],
figure[data-original]:hover{
opacity: 1;
}
I've also added a transition to enhance the experience.
Update
Version that follows mouse movements: http://jsfiddle.net/sandro_paganotti/k3AmZ/3/