Long text in tipsy tooltips - javascript

I'm happily using tipsy for my d3.js graphs, but now I noticed that tipsy is cutting off text if it's too long.
For instance the line containing the word CALCULATOR is cut off:
Is there a way to extend the width of the tooltip or autosize it according to the longest line of the tooltip text (I didn't find anything in the docs)?

I ended up writing my own tooltips module for D3 (see below). It uses require.js.
I use it like this:
define('myD3module',
// This module depends on the following modules
['tooltips', 'd3'],
function (tooltips, d3){
// Code omitted...
var node = svg.selectAll('g.node');
var nodeEnter = node.enter().append('g')
.on('mouseover', onMouseOver)
.on('mouseout', onMouseOut);
// Code omitted...
var onMouseOver = function(d){
tooltips.showTooltip(d, getTooltipText);
};
var onMouseOut = function(d){
tooltips.hideTooltip(d);
};
// Code omitted...
var getTooltipText = function(d){
var tipText = '';
var name = d.name;
if(typeof name != 'undefined'){
tipText += 'Name = ' + name + '<br>';
}
return tipText;
};
This is the module.
// This module shows tooltips near the user's cursor. The text of the tooltip must be provided by the consumer of this module. This module can be used with require.js.
define('tooltips',
// Required modules:
['d3','jquery'],
function (d3, $){
// Contains the tooltip. This div will be repositioned as needed.
var tooltip = d3.select('body').append('div')
.attr('class', 'tooltip')
.style('opacity', 0);
// This CSS styles the tooltip
var css = './config/tooltip.css';
var link = document.createElement('link');
link.type = 'text/css';
link.rel = 'stylesheet';
link.href = css;
document.getElementsByTagName('head')[0].appendChild(link);
var hideTooltip = function (d) {
tooltip.style('opacity', 0);
},
/** Show the tooltip. The text is defined by a function and the element the mouse cursor is placed on. */
showTooltip = function (elem, getTooltipText){
tooltip.style('opacity', 0.9);
var text = getTooltipText(elem);
var tipWidth = getTooltipWidth(text);
// Draw the tips below the mouse cursor
var tipYpos = d3.event.pageY + 20;
tooltip.html(text)
// The tooltip is centered below the cursor
.style('left', (d3.event.pageX - tipWidth/2) + 'px')
.style('top', tipYpos + 'px')
.style('width', tipWidth + 'px')
.style('height', getTooltipHeight(text) + 'px');
},
/**
* The tooltip spans all the text's lines vertically.
*/
getTooltipHeight = function (text) {
var totalHeight = 0;
var lines = text.split('<br>');
for (var i = 0; i < lines.length; i++){
var line = $.trim(lines[i]);
// Ignore empty strings
if(line.length > 0){
var span = document.createElement('span');
span.innerHTML = line;
$('body').append(span);
totalHeight += $(span).height();
$(span).remove();
}
}
return totalHeight;
},
/*
* The width of the tooltip is the width of the longest line in the tooltip text
*/
getTooltipWidth = function (text){
var lines = text.split('<br>');
// Append a dummy span containing the current line to the DOM. The browser calculates the width of that span
var maxWidth = d3.max(lines, function(line) {
var span = document.createElement('span');
span.innerHTML = line;
$('body').append(span);
var spanWidth = $(span).width();
$(span).remove();
return spanWidth;
});
return maxWidth;
};
// These functions are public
return {
showTooltip: showTooltip,
hideTooltip: hideTooltip
};
}
);
This is the tooltip.css:
.tooltip {
position: absolute;
text-align: center;
font: 12px sans-serif;
background-color: rgb(95,230,245);
border-radius: 2px;
pointer-events: none;
}

Related

Reduce font-size based on number of div lines

As the title says, I wanted the font to be reduced based on the number of lines in the div and from a start size. (Each line reduces the font).
The function below is all I have and works based on the height of the div.
$.fn.resizeText = function (options) {
var settings = $.extend({ maxfont: 40, minfont: 17 }, options);
var style = $('<style>').html('.nodelays ' +
'{ ' +
'-moz-transition: none !important; ' +
'-webkit-transition: none !important;' +
'-o-transition: none !important; ' +
'transition: none !important;' +
'}');
function shrink(el, fontsize, minfontsize)
{
if (fontsize < minfontsize) return;
el.style.fontSize = fontsize + 'px';
if (el.scrollHeight > el.closest(".container").offsetHeight) shrink(el, fontsize - 1, minfontsize);
}
$('head').append(style);
$(this).each(function(index, el)
{
var element = $(el);
element.addClass('nodelays');
shrink(el, settings.maxfont, settings.minfont);
element.removeClass('nodelays');
});
style.remove();
}
The size you are looking for is relative to the font family, because it is not always the same number of lines if they are tested with different fonts in the same text. using the following solution:
How can I count text lines inside an DOM element? Can I?
This is an approximate solution, taking into account a maximum number of lines
<body>
<div id="content" style="width: 80px; font-size: 20px; line-height: 20px; visibility: hidden;">
hello how are you? hello how are you? hello how are you? hello how are you? how are you? how are you? how are you?
</div>
<script>
function countLines(elm) {
var el = document.getElementById(elm);
var divHeight = el.offsetHeight;
var lineHeight = parseInt(el.style.lineHeight);
var lines = divHeight / lineHeight;
return lines;
}
var max_lines = 10;
function auto_size_text(elm) {
var el = document.getElementById(elm);
var lines = countLines(elm);
var size = parseInt(el.style.fontSize.replace("px", ""));
if(lines > max_lines) {
size--;
el.style.fontSize = size + "px";
el.style.lineHeight = size + "px";
auto_size_text(elm);
}
}
function show_div (elm) {
var el = document.getElementById(elm);
el.style.visibility = "";
}
auto_size_text("content");
show_div("content");
</script>
</body>

Get the focused image element inside a contenteditable div

I need to modify a pasted image inside a contenteditable div: resize it proportionately, add borders, etc... Perhaps this can be achieved by adding a className that will alter the necessary CSS properties.
The problem is that I don't know how to reference the focused, i.e. the active, the clicked-upon image or any element for that matter.
This is the HTML that I am trying to use
<!DOCTYPE html>
<html>
<body>
<div contenteditable="true">This is a div. It is editable. Try to change this text.</div>
</body>
</html>
Using css, html and javascript
Your editable content should be inside a parent div with an id or class name, you can even have different divs for images and such.
Then it is as simple as having css like so:
#editableContentDiv img {
imgProperty: value;
}
Then you can have javascript like so:
function insertImage(){
var $img = document.querySelector("#editableContentDiv img");
$img.setAttribute('class','resize-drag');
}
Eventually if you have more than one img inside the same div you can use querySelectorAll instead of querySelector and then iterate through the img tags inside same div.
I beleive that should at least give you an idea of where to start with what you want.
Similar example
I found this gist that seems to have similar things to want you want but a bit more complicated.
function resizeMoveListener(event) {
var target = event.target,
x = (parseFloat(target.dataset.x) || 0),
y = (parseFloat(target.dataset.y) || 0);
// update the element's style
target.style.width = event.rect.width + 'px';
target.style.height = event.rect.height + 'px';
// translate when resizing from top or left edges
x += event.deltaRect.left;
y += event.deltaRect.top;
updateTranslate(target, x, y);
}
function dragMoveListener(event) {
var target = event.target,
// keep the dragged position in the data-x/data-y attributes
x = (parseFloat(target.dataset.x) || 0) + event.dx,
y = (parseFloat(target.dataset.y) || 0) + event.dy;
updateTranslate(target, x, y);
}
function updateTranslate(target, x, y) {
// translate the element
target.style.webkitTransform =
target.style.transform =
'translate(' + x + 'px, ' + y + 'px)';
// update the position attributes
target.dataset.x = x;
target.dataset.y = y;
}
function insertImage() {
var $img = document.createElement('img');
$img.setAttribute('src', 'https://vignette.wikia.nocookie.net/googology/images/f/f3/Test.jpeg/revision/latest?cb=20180121032443');
$img.setAttribute('class', 'resize-drag');
document.querySelector(".container-wrap").appendChild($img);
var rect = document.querySelector(".container-wrap").getBoundingClientRect();
$img.style.left = rect.left;
$img.style.top = rect.top;
}
function dataTransfer() {
//cleanup
var $out = document.querySelector(".out-container-wrap");
while ($out.hasChildNodes()) {
$out.removeChild($out.lastChild);
}
//get source
var source = document.querySelector('.container-wrap')
//get data
var data = getSource(source);
//add data to target
setSource($out, data);
}
/**
* Get data from source div
*/
function getSource(source) {
var images = source.querySelectorAll('img');
var text = source.querySelector('div').textContent;
//build the js object and return it.
var data = {};
data.text = text;
data.image = [];
for (var i = 0; i < images.length; i++) {
var img = {}
img.url = images[i].src
img.x = images[i].dataset.x;
img.y = images[i].dataset.y;
img.h = images[i].height;
img.w = images[i].width;
data.image.push(img)
}
return data;
}
function setSource(target, data) {
//set the images.
for (var i = 0; i < data.image.length; i++) {
var d = data.image[i];
//build a new image
var $img = document.createElement('img');
$img.src = d.url;
$img.setAttribute('class', 'resize-drag');
$img.width = d.w;
$img.height = d.h;
$img.dataset.x = d.x;
$img.dataset.y = d.y;
var rect = target.getBoundingClientRect();
$img.style.left = parseInt(rect.left);
$img.style.top = parseInt(rect.top);
//transform: translate(82px, 52px)
$img.style.webkitTransform = $img.style.transform = 'translate(' + $img.dataset.x + 'px,' + $img.dataset.y + 'px)';
//$img.style.setProperty('-webkit-transform', 'translate('+$img.dataset.x+'px,'+$img.dataset.y+'px)');
target.appendChild($img);
}
//make a fresh div with text content
var $outContent = document.createElement('div')
$outContent.setAttribute('class', 'out-container-content');
$outContent.setAttribute('contenteditable', 'true');
$outContent.textContent = data.text;
target.appendChild($outContent);
}
interact('.resize-drag')
.draggable({
onmove: dragMoveListener,
inertia: true,
restrict: {
restriction: "parent",
endOnly: true,
elementRect: {
top: 0,
left: 0,
bottom: 1,
right: 1
}
}
})
.resizable({
edges: {
left: true,
right: true,
bottom: true,
top: true
},
onmove: resizeMoveListener
})
.resize-drag {
z-index: 200;
position: absolute;
border: 2px dashed #ccc;
}
.out-container-content,
.container-content {
background-color: #fafcaa;
height: 340px;
}
#btnInsertImage {
margin-bottom: 10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="http://code.interactjs.io/interact-1.2.4.min.js"></script>
</head>
<body>
<button id="btnInsertImage" onclick="insertImage()">Insert Image</button>
<div class="container-wrap">
<div class="container-content" contenteditable="true">This is the content of the container. The content is provided along with the image. The image will be moved around and resized as required. The image can move within the boundary of the container.</div>
</div>
<button id="btnSubmit" onclick="dataTransfer()">Submit</button>
<div class="out-container-wrap">
</div>
</body>
</html>
Source
This is what I tried and it seems to be working - at least on my system, the snippet I made does not work unfortunately.
function getSelImg(){
var curObj;
window.document.execCommand('CreateLink',false, 'http://imageselectionhack/');
allLinks = window.document.getElementsByTagName('A');
for(i = 0; i < allLinks.length; i++)
{
if (allLinks[i].href == 'http://imageselectionhack/')
{
curObj = allLinks[i].firstChild;
window.document.execCommand('Undo'); // undo link
break;
}
}
if ((curObj) && (curObj.tagName=='IMG'))
{
//do what you want to...
curObj.style.border = "thick solid #0000FF";
}
}
<!DOCTYPE html>
<html>
<body>
<input type="button" onclick="getSelImg()" value="set img border">
<div contenteditable="true">This is a div.<br>
It is editable.<br>
Try to paste an image here:<br>
###########################<br>
<br>
<br>
<br>
###########################
</div>
</body>
</html>

How can you target part of svg image

So I have this similar svg image. white being transparent with black dots. I have to cover the whole background with this pattern and then target a 5x5square dot area of dots to change their color.
What is the simplest way or a common method to achieve this?
You can access elements of an SVG-image if it is embedded directly into the HTML-code. You can even create the whole SVG using JavaScript. Here is an example:
https://jsfiddle.net/da_mkay/eyskzpsc/7/
<!DOCTYPE html>
<html>
<head>
<title>SVG dots</title>
</head>
<body>
</body>
<script>
var makeSVGElement = function (tag, attrs) {
var element = document.createElementNS('http://www.w3.org/2000/svg', tag)
for (var k in attrs) {
element.setAttribute(k, attrs[k])
}
return element
}
var makeDotSVG = function (width, height, dotRadius) {
var svg = makeSVGElement('svg', { width: width, height: height, 'class': 'dot-svg' })
, dotDiameter = dotRadius*2
, dotsX = Math.floor(width / dotDiameter)
, dotsY = Math.floor(height / dotDiameter)
// Fill complete SVG canvas with dots
for (var x = 0; x < dotsX; x++) {
for (var y = 0; y < dotsY; y++) {
var dot = makeSVGElement('circle', {
id: 'dot-'+x+'-'+y,
cx: dotRadius + x*dotDiameter,
cy: dotRadius + y*dotDiameter,
r: dotRadius,
fill: '#B3E3A3'
})
svg.appendChild(dot)
}
}
// Highlight the hovered dots by changing its fill-color
var curMouseOver = function (event) {
var dotX = Math.floor(event.offsetX / dotDiameter)
, dotY = Math.floor(event.offsetY / dotDiameter)
, dot = svg.getElementById('dot-'+dotX+'-'+dotY)
if (dot !== null) {
dot.setAttribute('fill', '#73B85C')
}
}
svg.addEventListener('mouseover', curMouseOver)
return svg
}
// Create SVG and add to body
var myDotSVG = makeDotSVG(500, 500, 5)
console.log(document.body)
document.body.appendChild(myDotSVG)
</script>
</html>

adding text to overlay in javascript

I've used some source for a transparent overlay in JavaScript:
function grayOut(vis, options)
{
var options = options || {};
var zindex = 50;
var opacity = 70;
var opaque = (opacity / 100);
var bgcolor = options.bgcolor || '#000000';
var dark=document.getElementById('darkenScreenObject');
if (!dark) {
// The dark layer doesn't exist, it's never been created. So we'll
// create it here and apply some basic styles.
// If you are getting errors in IE see: http://support.microsoft.com/default.aspx/kb/927917
var tbody = document.getElementsByTagName("body")[0];
var tnode = document.createElement('div'); // Create the layer.
tnode.style.position='absolute'; // Position absolutely
tnode.style.top='0px'; // In the top
tnode.style.left='0px'; // Left corner of the page
tnode.style.display='none'; // Start out Hidden
tnode.id='darkenScreenObject'; // Name it so we can find it later
tbody.appendChild(tnode);
/*
var pTag = document.createElement("P");
var txtProcessing = document.createTextNode("Processing GIF...");
tnode.appendChild(txtProcessing);
*/
}
if (vis)
{
// Calculate the page width and height
if( document.body && ( document.body.scrollWidth || document.body.scrollHeight ) )
{
var pageWidth = document.body.scrollWidth+'px';
var pageHeight = document.body.scrollHeight+'px';
}
else if( document.body.offsetWidth )
{
var pageWidth = document.body.offsetWidth+'px';
var pageHeight = document.body.offsetHeight+'px';
}
else
{
var pageWidth='100%';
var pageHeight='100%';
}
//set the shader to cover the entire page and make it visible.
dark.style.opacity=opaque;
dark.style.MozOpacity=opaque;
dark.style.filter='alpha(opacity='+opacity+')';
dark.style.zIndex=zindex;
dark.style.backgroundColor=bgcolor;
dark.style.width= pageWidth;
dark.style.height= pageHeight;
dark.style.display='block';
var txt = document.createTextNode("This text was added.");
dark.appendChild(txt);
}
else
{
dark.style.display='none';
}
}
My problem is I'm trying to get some text to show up on the transparent layer but I can't get it to work. Any thoughts?
Your text node is created on overlay but is invisible cause of text color.
check Fiddle where text color is set to red.
dark.style.color = 'red';

Fix node width and height in Spacetree from Javascript Infovis Toolkit

I've finally got my nodes almost done perfectly, unfortunately I'm having one more problem
the width of what is drawn on the canvas isn't the width of the defined node. The blue + purple is the node div + padding, and I could perfectly center it using that if it weren't for the fact that what is drawn doesn't care about the width I have for that. Here's my code for my spacetree:
function jitSpaceTree(data,index,rootid){
var json = eval("(" + data + ")");
console.log(json);
//end
//init Spacetree
//Create a new ST instance
var st = new $jit.ST({
//id of viz container element
injectInto: 'hier'+index,
//set duration for the animation
duration: 800,
//set animation transition type
transition: $jit.Trans.Quart.easeInOut,
//set distance between node and its children
levelDistance: 25,
orientation: 'top',
//enable panning
Navigation: {
enable:true,
panning:true
},
//set node and edge styles
//set overridable=true for styling individual
//nodes or edges
Node: {
autoHeight: true,
autoWidth: true,
type: 'rectangle',
color: '#aaa',
overridable: true
},
Edge: {
type: 'bezier',
overridable: true
},
//This method is called on DOM label creation.
//Use this method to add event handlers and styles to
//your node.
onCreateLabel: function(label, node){
label.id = node.id;
label.innerHTML = node.name;
label.onclick = function(){
st.onClick(node.id);
st.select(node.id);
st.removeSubtree(label.id, false, "replot", {
hideLabels: false
});
jQuery.getJSON('Mobile_Subordinate.cfm?Empid='+node.id, function(data2) {
var subtree = '';
for(var i=0; i<data2.DATA.length-1; i++){
subtree = subtree + '{"id": "' + data2.DATA[i][4].replace(/\s/g, '') + '","name": "' + data2.DATA[i][0].replace(/\s/g, '') + '<br>' + data2.DATA[i][1].replace(/\s/g, '') + '","data": {},"children": []},';
}
subtree = subtree + '{"id": "' + data2.DATA[data2.DATA.length-1][4].replace(/\s/g, '') + '","name": "' + data2.DATA[data2.DATA.length-1][0].replace(/\s/g, '') + '<br>' + data2.DATA[data2.DATA.length-1][1].replace(/\s/g, '') + '","data": {},"children": []}';
subtree = '{"id": "'+label.id+'", "children": ['+ subtree +']}'
childData = jQuery.parseJSON(subtree);
console.log(childData);
st.addSubtree(childData, 'replot',{
hideLabels: false
});
});
};
//set label styles
var style = label.style;
style.width = node.data.offsetWidth;
style.height = node.data.offsetHeight;
style.cursor = 'pointer';
style.color = '#fff';
style.fontSize = '0.8em';
style.textAlign= 'center';
},
//This method is called right before plotting
//a node. It's useful for changing an individual node
//style properties before plotting it.
//The data properties prefixed with a dollar
//sign will override the global node style properties.
onBeforePlotNode: function(node){
//add some color to the nodes in the path between the
//root node and the selected node.
if (node.selected) {
node.data.$color = "#ab8433";
}
else {
delete node.data.$color;
node.data.$color = "#ccc";
}
},
//This method is called right before plotting
//an edge. It's useful for changing an individual edge
//style properties before plotting it.
//Edge data proprties prefixed with a dollar sign will
//override the Edge global style properties.
onBeforePlotLine: function(adj){
if (adj.nodeFrom.selected && adj.nodeTo.selected) {
adj.data.$color = "#eed";
adj.data.$lineWidth = 3;
}
else {
delete adj.data.$color;
delete adj.data.$lineWidth;
}
}
});
//load json data
st.loadJSON(json);
//compute node positions and layout
st.compute();
//optional: make a translation of the tree
st.geom.translate(new $jit.Complex(-200, 0), "current");
//emulate a click on the root node.
//st.onClick(st.root);
st.onClick(rootid);
//end
}
What am I missing?
Not an exact answer but could this be a padding issue?
I know from my own experience that
Node: {
height: 80,
width: 140
}
needs...
{style.width = 136 + 'px';
style.height = 75 + 'px';
style.paddingTop = '5px';
style.paddingLeft = '2px';
style.paddingRight = '2px';}
to center align.
I wonder if there is a default padding you're missing?

Categories