Manually position an opentip tooltip in combination with Kinetic.js - javascript

I'm making a web application that uses Kinetic.js for some fancy
graphical functions, and I want to show a tooltip with some information about each element when the users hovers over it.
I've already figured out how to invoke a function on hover with Kinetic.js:
myKineticObject.on("mousemove", function() {
// Change content of tooltip depending on myKineticObject
// Set position of tooltip to cursor position
// Show tooltip
});
myKineticObject.on("mouseout", function() {
// Hide tooltip
}
I've decided to use the seemingly nice Opentip to show the tooltip.
The only problem is that Kinetic.js doesn't create any usable DOM elements to use as a target for opentip.
This is (roughly) what my HTML looks like:
<html>
<head><!-- Styles --></head>
<body>
<div id="container">
<canvas class = "kineticjs-buffer-layer">
<canvas class = "kineticjs-path-layer">
<canvas class = "myLayer1">
<canvas class = "myLayer2">
<!-- ... more layers -->
</div>
<!-- Scripts -->
</body>
</html>
Important to know is that these canvas elements all have the same width and height and stack on eachother. So they're unusable as targets.
So instead of using a DOM element to use as the target for my tooltip, I need to
manually show/hide and position the tooltip.
I've figured out how to do the showing and hiding like this:
var tooltip = new Opentip(
"div#container", //target element
"DummyContent", // will be replaced
"My Title", // title
{
showOn: null, // I'll manually manage the showOn effect
});
I now do the following:
myKineticObject.on("mousemove", function() {
tooltip.show();
});
myKineticObject.on("mouseout", function() {
tooltip.hide();
}
The only problem is that it just shows up at the top left of the page, and I can't find anything in the docs on how to position this thing manually.
Suggestions or ideas welcome. I'm also open to using a different tooltip library, if necessary.
Thanks!

It appears (with no knowledge of Opentip, besides a quick look at the docs) that you can set 'fixed' to true in the config. Since the target is null, the docs suggest fixed=true should make the tooltip appear at the mouse position, but not follow it once the mouse is moved around.
How does that sound?

I managed to solve the problem like this, for those of you who are looking for a solution too:
group.on("mousemove", function(event) {
tooltip.content = //change content
tooltip.show();
$("#opentip-1").offset({ left: event.offsetX, top: event.offsetY });
});

Related

Can't add elements with SnapSVG to my Page

So I am using SnapSVG to manipulate (animate) my SVG File that i created with Inkscape. The animating of the different elements like a rectangle or circle is working completely fine. The problem that i have is when i try to add a new element like a rectangle with SnapSVG code it does not work and the whole screen is blank (everything disappears). Why do i have this problem? Is it even possible to add a new element with SnapSVG to a existing SVG File?
Down below i showed you some code on how i manipulate the SVG and how it gets displayed in a DIV on my Page. And I am also showing you how I am trying to add a new Element with SnapSVG
I tried almost everything. I can put the code for the new Element outside of the code for the already existing SVG but then it always appears outside of the SVG file.
<head>
<meta charset="utf-8">
<title>Inkscape Animated Icon Snap</title>
<!--We need to add the Snap.svg script to our document-->
<script src="snap.svg.js"></script>
<script>
//Run script right away
window.onload = function () {
//We'll be appending the icon to this DIV later
var s = Snap("#svgDiv");
//Have Snap load the SVG file
Snap.load("icon.svg", function(f) {
s.append(f);
//Assign the white rectangle
whiteRect = f.select("#whiteRect");
//Assign the whole icon group
icon = f.select("#icon");
//When the icon is hovered over, have the white rectangle move up slightly with elastic properties
icon.hover(function() {
whiteRect.animate({y:18}, 500, mina.elastic);
},
//And return to original position when not hovered over
function() {
whiteRect.animate({y:23.984177}, 500, mina.elastic);
}
);
var bigCircle = s.circle(0, 0, 20).attr({fill: 'green' });
icon.append(bigCircle);
//Finally append the icon to iconDiv in the body
});
};
</script>
</head>
<body>
<!--Here's the DIV that will hold the animated SVG icon-->
<svg id="svgDiv"></svg>
</body>
So basically what I want as a result is just another rectangle added to my SVG File. What i get is a blank Page.
2 main things spring to mind (hard to be sure without the html/svg as well).
Firstly, you Snap must be called on an SVG element, not an HTML element, so when I see this line...
var s = Snap("#iconDiv");
it's almost certainly wrong. It cannot be a div, or any HTML element. Snap works on SVG elements,
var s = Snap("#anSVGselement");
is what's needed.
Secondly, your line
s.append(f);
Wants to be straight after the Snap.load()
Snap.load("icon.svg", function(f) {
// stuff here probably won't work, there are some minimal methods like select, but not like hover etc
s.append(f);
//do stuff now it's appended
var icon = s.select("#icon");
var bigCircle = s.circle(10, 970, 20);
icon.append(bigCircle);
});
The problem is, f is just a fragment at this point, it has no place in the DOM yet, so methods like 'hover' etc, aren't available until you have added with append into the DOM. Once it's appended these methods become available. There are a few methods available like select, before it's appended (so you 'may' be able to select an element from the fragment and then append that, rather than appending everything I think).
Example
Also note, the icon.svg file has some transforms on it, so you will need to adjust the circle to have the correct cx/cy like in my example, or add a transform to match (or remove the transforms from the original svg and have correct cx/cy)

Show hidden image on mouseover

I have a problem when trying achieve hover effect on mapped image. I have an image with mapped areas and on all of them I want to show a different image when hover.
You can see my work so far here:
http://mantasmilka.com/map/pries-smurta.html
The problem is when I hover over area it show the image, but when I move the cursor (not leaving the area) it starts flickering. It takes area space pixel by pixel.
I've tried working with Javascript and jQuery solutions:
Javascript:
mouseenter="document.getElementById('Vilnius').style.display = 'block';" mouseleave="document.getElementById('Vilnius').style.display = 'none';"
jQuery:
$('.hide').hide();
setTimeout(function(){
$("#area-kaunas").mouseenter(function(){
$('#Kaunas').show();
});
$("#area-kaunas").mouseleave(function(){
$('#Kaunas').hide();
});
}, 500);
Why not just use hover() inside of jQuery? I'm also unsure why you bind the events after a 500 millisecond timeout?
$('.hide').hide();
$("#area-kaunas").hover(function() {
$('#Kaunas').show();
}, function() {
$('#Kaunas').hide();
});
There is a css property called "pointer-event" which gives the value "none" to the img tags that overlap in the mapped image and works as you need it. This is the documentation https://developer.mozilla.org/en/docs/Web/CSS/pointer-events
The problem will always be the compatibility of browsers.

TinyMce submenu's not sticking to toolbar when using fixed_toolbar_container and absolute position

We would like to have a greater control of where and how we position the tinymce toolbar. We found this option fixed_toolbar_container which solves a lot for us but brings us an anoying problem. The documents say the fixed_toolbar_container (http://www.tinymce.com/wiki.php/Configuration:fixed_toolbar_container) can be used to have a fixed toolbar. But we actually would like to use it to be absolute so we can position it relative to it's container.
I created a JS Fiddle to demonstrate the problem: http://jsfiddle.net/ronfmLym/2/. When you open the toolbar by clicking on the text the toolbar will be positioned absolute. When you open a submenu (i.e. by clicking on "file") a submenu will open. Now when you start scrolling the submenu won't stick to the toolbar. This is because these submenu's get the mce-fixed class because we set the fixed_toolbar_container property.
<div class="element">
<div class="toolbar-container"></div>
<div class="content">
<p>Text</p>
</div>
</div>
Is there any way to make the submenu's stick to the toolbar when positioned absolute and scrolling? Keep in mind that we are switching to a fixed positioning when the toolbar is going off screen.
We thought we could maybe fix it by modifying the container element of de submenu's by using the piece of code below and overwriting the top-position of the submenu's and setting the positioner to absolute with css. But that seems to mess up the tooltips and tinymce doesn't recalculate the "left" css-property of the submenu's so the position in still off.
tinymce.ui.Control.prototype.getContainerElm = function() {
return document.getElementById('toolbar-container');
};
The only corresponding question I could find on stackoverflow was this one: TinyMCE push down submenus using fixed_toolbar_container, no answers there.
Tried wrapping the toolbar in a div and using position:relative; to try and hack it together, but didn't cooperate this time.
It appears that the toolbar actually is accounting for its position at the time of click. So your only conflict is if the opened toolbar is position:absolute and then changes to position:fixed or vice versa.
Your best [manual] bet would be to call a function at the same time that you change the position of the toolbar that:
Detects if any menus are open.
Changes the toolbar position.
Reopens the menus that were open.
The lazy (discouraged) fix would be to close all submenus whenever the position changes. This will fix the layout, but it will require the user to click once again to bring the menu back.
Sorry this isn't a silver bullet answer :(
This answer follows Brian John's suggestion:
I'm using this method to position any open mce-floatpanel (This is typescript, but it shouldn't be too hard to convert to ES or whatever you need.):
positionTinyMceDropdowns() {
// TODO: You'll need to replace all occurrences
// of this.mceWrapperElement with whatever is
// wrapping your TinyMCE. If you have only a
// single instance, you can just replace it
// with document
const button = <HTMLElement> this.mceWrapperElement.getElementsByClassName('mce-opened').item(0);
const items = document.getElementsByClassName('mce-floatpanel');
let wrapperNode: HTMLElement;
for (let i = 0; i < items.length; i++) {
const currentItem = <HTMLElement> items.item(i);
if (currentItem.style.display !== 'none') {
wrapperNode = currentItem;
break;
}
}
if (!wrapperNode || !button) {
return;
}
const styles = wrapperNode.style;
styles.display = 'block';
styles.position = 'absolute';
const bodyRect = document.body.getBoundingClientRect();
const buttonRect = button.getBoundingClientRect();
// get absolute button position:
let y = buttonRect.top - bodyRect.top;
y += 33; // toolbar line height;
styles.top = `${Math.floor(y)}px`;
}
The instances I found in which it needs to be called:
on window scroll (or if the editor is wrapped in a scrolling container, then whenever that scrolls)
on window resize (or if the editor is wrapped in a container that resizes without the window being resized, then whenever that container is resized)
So here's a sample for the simplest case in angular (again, adept to whichever js framework you're using):
import { HostListener } from '#angular/core';
// ...
#HostListener('window:resize', ['$event'])
#HostListener('window:scroll', ['$event'])
public onResize() {
this.positionTinyMceDropdowns();
}
Interestingly on iOS devices (and perhaps other mobile devices?) mce-floatpanel wasn't even positioned correctly after it had just been opened. So I had to add this:
tinymceConfig.setup = (editor: TinyMceEditor) => {
editor.on('init', () => {
const panel = this.mceWrapperElement.querySelector('.mce-tinymce.mce-panel');
if (panel) {
panel.addEventListener('touchend', () => {
this.positionTinyMceDropdowns();
});
}
});
};
I think the config setting fixed_toolbar_container is poorly explained in TinyMCE 6 documentation but when you correctly configure it, you'll find it will work much more nice (especially for inline mode) than the default configuration that tries to emulate position:sticky.
In practice, you want to fixed_toolbar_container set to a string that's CSS selector for the container, typically something like "#mycontainer".
After that, you can move the container element using its CSS properties, the TinyMCE user interface nicely follows around. (Modulo typical TinyMCE bugs, of course. For example, the submenus overflow to right with very narrow viewports.)
Note that TinyMCE positions stuff where it uses position:absolute relative to fixed_toolbar_container container and if you move that container around, in some cases you must execute editor.dispatch("ResizeWindow") to trigger TinyMCE to re-calculate the absolutely positioned elements.
See demo using custom container with position:sticky at https://jsfiddle.net/8bndv26t/1/.

amCharts doesn't display chart for initially-hidden divs

I am using amCharts (which uses Raphaƫl behind the scenes) to render some charts as SVG; and have noticed that if the SVG is rendered in an initially-invisible div, the browser does not immediately render the image when the div becomes visible. If I modify the display however, e.g. by resizing the browser or Ctrl-mousewheel zooming, the SVG image is then rendered as expected when the page is redrawn.
The exact method of div visibility switching is via Bootstrap's tabbed navbar.
I admit to not being very experienced with SVG - is this an issue with the browsers' rendering, or amCharts' SVG markup, or am I required to explicitly call some sort of repaint method when I can tell the visibility of an SVG has changed?
Here's a jsFiddle which illustrates the problem; if you switch to Section 2 (in Chrome, Firefox) the chart isn't visible initially. Resizing the display causes it to appear.
I've found the reason for both the initial behaviour and the workaround - and it's all amCharts specific (nothing to do with SVG per se) so I'm rephrasing the title accordingly.
What happens is that when amCharts creates the SVG, it needs to (or at least, decides to) define the width and height in absolute terms. These are based on the size of the target div, obtained via the offsetWidth and offsetHeight properties.
The inactive tab has the display: none property set, and as a result this part of the DOM is not even rendered, so returns zero for both size properties. This ultimately leads to amCharts creating a 0x0 SVG chart when chart.write is called for the hidden div.
Resizing fixes things because each chart registers a listener to the onresize window event, which calls the chart's handleResize method. This forces a recalculation of the width and height based on the div's new (current) dimensions.
So in conclusion I think there are two alternative ways to handle this:
Call chart.write for a chart when and only when its tab becomes visible.
Call each chart's handleResize method when the tabs change.
(The first option avoids the initial hit of rendering an invisible chart, but then does a full redraw every time the tabs are changed. The latter takes a hit up-front but is likely quicker thereafter. For bonus marks, the best solution would be to render each chart exactly once between each resize, the first time it becomes visible, but that's a lot more complex as it would involve interfering with the default event listeners, amongst other things.)
Update: There's further complications with rendering an invisible chart; in particular, I found issues with the height calculations not taking into account the space required by the domain axis and so stretching the chart out of its div. This wasn't fixed by calling handleResize - calling measureMargins beforehand looked like it should work but didn't. (There's probably another method one could call after this to make it work such as resetMargins but at this point it started to feel very flaky...)
As such I don't think it's practical to render a chart for the first time on a non-visible div, so I went with some combination of the bullets above. I listen for when a chart's tab becomes visible for the first time and then call chart.write for the appropriate chart object - and whenever the tabs change, all previously-rendered charts are told to handle the resize.
* Edited *
Here is a updated fiddle. The Canvas will only be rendered once the tab is shown.
I store the chartdiv ids in an array and check whether there are in it or not.
* Edited *
The only solution I found was to show the Graph after the specific tab is shown.
As you see in this jsFiddle.
var tabs = $('.tabbable').tab()
tabs.on('shown', function(e){
id = $(e.target).attr('href');
chartdiv_id = $(id).find('.chartdiv').attr('id');
doChart(chartdiv_id, true);
});
I guess it isn't exactly what you are looking for, but i hope it helps for the moment.
I had the same problem, but my solution it's alternative to display:none, you can use this class in the css
.hidden {
position: absolute !important;
top: -9999px !important;
left: -9999px !important;
}
this dissapear of the screen but visible for the amchart, so the resolution of the chart never lose the size!!!!
I completely agree with Andrzej Doyle.
Issuing handleresize on the chart when clicking on the selected div (tab) works for me on cs-cart with custom tabs (not jquery ones).
The following works while cart beeing globally defined.
function refreshchart(){
chart.handleResize();
};
I also ran into the issue and fixed it by making the initializer a function. Working fiddle.
$("#button").click(function(){
$("#chartdiv").toggle();
makeChart();
});
function makeChart() {
var chart = AmCharts.makeChart("chartdiv", {
//all the stuff
});
}
This Might help you to resolve issue . I have my amchart showing in different tab pan . SVG Component does not allow them to show that div due to resizing issue .
$(window).resize(function() {
setTimeout(function() {
chart.write("chartdiv1");
}, 300);
});
resize again your window while you create your charts ..
Show me charts
<div class="charts_div" style="display:hidden;">
some charts here
</div>
<script>
$(document).on('click', '.show_charts', function(){
$('.charts_div').toggle();
//just redraw all charts available on the page
for(var i = 0; i < AmCharts.charts.length; i++) {
AmCharts.charts[i].validateData();
}
});
</script>
var chart = null;
AmCharts.ready(function () {
chart = AmCharts.makeChart('chart', .....);
});
$(document).ready(function(){
$("#view_chart").click(function(){
chart.validateSize();
});
});
<button type="button" id="view_chart">View Chart</button>
<div style="display:none;" id="chart"></div>
Another work around would be
drawTransactionTypeChart();
setTimeout(function(){hidePieCharts();},1);
Initially set display:inline to div which is chart container, it gets rendered .
Then set display:none using setTimeout() after 1ms.
Hope this helps...
I have two amcharts on different tabs.
A stock chart to be place on #chartdiv and a pie chart to be placed on #chartdivpie.
This is how I solved my problem.
My custom css - to overwrite bootstrap -
#chartdivpie { width: 1138px; height: 500px; }
.tab-content .tab-pane {
position: absolute;
top: -9999px;
left: -9999px;
display: inline;
}
.tab-content .tab-pane.active {
position: inherit !important;
}
JQuery call
$('#myTab a').click(function (e) {
e.preventDefault()
$(this).tab('show');
chart.invalidateSize();
chart.write('chartdiv');
})

Scrollable div without overflow:auto?

In my app I have 2 divs, one with a long list of products that can be dragged into another div (shopping cart). The product div has the overflow but it breaks prototype draggable elements. The prototype hacks are very obtrusive and not compatible with all browsers.
So I am taking a different approach, is it possible to have a scrollable div without using CSS overflow:auto?
Theres a css property to control that.
<div style="width:100px;height:100px;overflow:scroll">
</div>
http://www.w3schools.com/Css/pr_pos_overflow.asp
You can use a frame with content larger than its window. Might make it hard to pass JS events though.
Here is what I wrote to have it running under IE 8.0.6 & Firefox 3.6.3:
Make draggable the elements (with border) in the "width:100px;scrollable:auto" container:
function makeDraggable(container,tag) {
if(!container || !tag) { return false; }
$(container).select(tag).each( function(o) {
new Draggable(o,{
starteffect: function(e){makeDragVisible(container,e);},
endeffect: function(e){e.setStyle({'position':'','width':'','cursor':''});},
zindex: 1000
// , revert: ... // the other options
});
});
}
function makeDragVisible(container,element) {
if(!container || !element) { return false; }
var i=$(container).getStyle('width');
i=i.replace('px','');
i=Math.round(i-20)+'px';
element.setStyle({'width':i,'z-index':1000,'position':'absolute','cursor':'move'});
//
$(container).setStyle({});
}
Important notes:
the z-index is repeated
notice the container loss of style at the end of 'starteffect'. Cursor and width are simply there to keep the drag user friendly.
I hope it helps.

Categories