Related
I'm gonna try to explain what is happening.
When I use a class "hide" as a display: none; to hide a button and show another button after click it works well in desktop browser, but when I tried the same thing at devices like iPhone, Tablets, etc. It has a strange behavior, after the second click, apparently it works ok, but the button don't change, it only changes when I click outside of it. If I don't do that looks like the button do not change. I have to click at the button one time to change them and apparently works, but the button only change when I click again outside it. It happens only in mobile devices.
I don't know what can I do to fix this in devices.
const buttonForest = document.querySelector(".button-forest");
const buttonForestSelected = document.querySelector(".button-forest-selected");
function forestButton() {
buttonForest.classList.remove("hide");
buttonForestSelected.classList.add("hide");
}
function forestButtonSelected() {
buttonForest.classList.add("hide");
buttonForestSelected.classList.remove("hide");
}
buttonForest.addEventListener("click", function() {
forestButtonSelected();
});
buttonForestSelected.addEventListener("click", function() {
forestButton();
});
:root {
font-size: 62.5%;
}
main {
display: flex;
align-self: center;
justify-self: center;
padding: 5rem;
}
button {
background-color: transparent;
border: none;
cursor: pointer;
position: relative;
display: flex;
align-items: end;
justify-content: center;
}
.button-container {
border: none;
position: relative;
display: flex;
align-items: end;
justify-content: center;
}
.icon-sound:hover .slider {
background: white;
}
.icon-sound:hover .slider::-webkit-slider-thumb {
background: white;
}
.icon-sound:hover .slider::-moz-range-thumb {
background: white;
}
.button-container {
border: none;
position: relative;
display: flex;
align-items: end;
justify-content: center;
}
.hide {
display: none;
}
.icon-sound svg path {
fill: #e1e1e6;
}
.icon-sound svg path+path {
fill: #323238;
}
.icon-sound:hover .cor1 {
fill: #02799d;
}
.icon-sound:hover .cor2 {
fill: white;
}
#sounds {
display: grid;
grid-template-columns: auto auto;
gap: 3.2rem;
grid-template-areas: "item1 item2" "item3 item4";
}
.button-forest-selected .cor1 {
fill: #02799d;
}
.button-forest-selected .cor2 {
fill: white;
}
<div class="button-container icon-sound">
<button class="button-forest">
<svg
width="138"
height="152"
viewBox="0 0 138 152"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
class="cor1"
d="M0 24C0 10.7452 10.7452 0 24 0H114C127.255 0 138 10.7452 138 24V128C138 141.255 127.255 152 114 152H24C10.7452 152 0 141.255 0 128V24Z"
fill="#E1E1E6"
/>
<path
class="cor2"
d="M51.8355 104V99.8H67.0329V87.2H60.8158C56.9934 87.2 53.7352 85.835 51.0411 83.105C48.347 80.375 47 77.0733 47 73.2C47 70.4 47.7599 67.8217 49.2796 65.465C50.7993 63.1083 52.8487 61.3933 55.4276 60.32C55.8421 56.82 57.3503 53.8917 59.9523 51.535C62.5543 49.1783 65.6053 48 69.1053 48C72.6053 48 75.6562 49.1783 78.2582 51.535C80.8602 53.8917 82.3684 56.82 82.7829 60.32C85.3618 61.3933 87.4112 63.1083 88.9309 65.465C90.4507 67.8217 91.2105 70.4 91.2105 73.2C91.2105 77.0733 89.8635 80.375 87.1694 83.105C84.4753 85.835 81.2171 87.2 77.3947 87.2H71.1776V99.8H87.0658V104H51.8355ZM60.8158 83H77.3947C80.0658 83 82.3454 82.0433 84.2336 80.13C86.1217 78.2167 87.0658 75.9067 87.0658 73.2C87.0658 71.24 86.5132 69.455 85.4079 67.845C84.3026 66.235 82.875 65.0333 81.125 64.24L78.9145 63.26L78.6382 60.81C78.3158 58.3367 77.2566 56.2833 75.4605 54.65C73.6645 53.0167 71.5461 52.2 69.1053 52.2C66.6645 52.2 64.5461 53.0167 62.75 54.65C60.9539 56.2833 59.8947 58.3367 59.5724 60.81L59.2961 63.26L57.0855 64.24C55.3355 65.0333 53.9079 66.235 52.8026 67.845C51.6974 69.455 51.1447 71.24 51.1447 73.2C51.1447 75.9067 52.0888 78.2167 53.977 80.13C55.8651 82.0433 58.1447 83 60.8158 83Z"
fill="#323238"
/>
</svg>
</button>
<button class="button-forest-selected hide">
<svg
width="138"
height="152"
viewBox="0 0 138 152"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
class="cor1"
d="M0 24C0 10.7452 10.7452 0 24 0H114C127.255 0 138 10.7452 138 24V128C138 141.255 127.255 152 114 152H24C10.7452 152 0 141.255 0 128V24Z"
fill="#E1E1E6"
/>
<path
class="cor2"
d="M51.8355 104V99.8H67.0329V87.2H60.8158C56.9934 87.2 53.7352 85.835 51.0411 83.105C48.347 80.375 47 77.0733 47 73.2C47 70.4 47.7599 67.8217 49.2796 65.465C50.7993 63.1083 52.8487 61.3933 55.4276 60.32C55.8421 56.82 57.3503 53.8917 59.9523 51.535C62.5543 49.1783 65.6053 48 69.1053 48C72.6053 48 75.6562 49.1783 78.2582 51.535C80.8602 53.8917 82.3684 56.82 82.7829 60.32C85.3618 61.3933 87.4112 63.1083 88.9309 65.465C90.4507 67.8217 91.2105 70.4 91.2105 73.2C91.2105 77.0733 89.8635 80.375 87.1694 83.105C84.4753 85.835 81.2171 87.2 77.3947 87.2H71.1776V99.8H87.0658V104H51.8355ZM60.8158 83H77.3947C80.0658 83 82.3454 82.0433 84.2336 80.13C86.1217 78.2167 87.0658 75.9067 87.0658 73.2C87.0658 71.24 86.5132 69.455 85.4079 67.845C84.3026 66.235 82.875 65.0333 81.125 64.24L78.9145 63.26L78.6382 60.81C78.3158 58.3367 77.2566 56.2833 75.4605 54.65C73.6645 53.0167 71.5461 52.2 69.1053 52.2C66.6645 52.2 64.5461 53.0167 62.75 54.65C60.9539 56.2833 59.8947 58.3367 59.5724 60.81L59.2961 63.26L57.0855 64.24C55.3355 65.0333 53.9079 66.235 52.8026 67.845C51.6974 69.455 51.1447 71.24 51.1447 73.2C51.1447 75.9067 52.0888 78.2167 53.977 80.13C55.8651 82.0433 58.1447 83 60.8158 83Z"
fill="#323238"
/>
</svg>
</button>
</div>
Hellol,I have a code for a custom cursor, and the cursor, which is a ball/circle, was supposed to grow/scale when hovering over a link, if you see the code below, this function is there, but it is not working, does anyone know what's wrong? Thank you in advance. Note, I am unable to create a snippet here. The code is from codepen: https://codepen.io/clementGir/pen/RQqvQx
<div class="cursor">
<div class="cursor__ball cursor__ball--big ">
<svg height="30" width="30">
<circle cx="15" cy="15" r="12" stroke-width="0"></circle>
</svg>
</div>
<div class="cursor__ball cursor__ball--small">
<svg height="10" width="10">
<circle cx="5" cy="5" r="4" stroke-width="0"></circle>
</svg>
</div>
</div>
<style>
body .cursor {
pointer-events: none;
}
body .cursor__ball {
position: fixed;
top: 0;
left: 0;
mix-blend-mode: difference;
z-index: 1000;
}
body .cursor__ball circle {
fill: #f7f8fa;
}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.3/TweenMax.min.js"></script>
<script>
const $bigBall = document.querySelector('.cursor__ball--big');
const $smallBall = document.querySelector('.cursor__ball--small');
const $hoverables = document.querySelectorAll('a');
// Listeners
document.body.addEventListener('mousemove', onMouseMove);
for (let i = 0; i < $hoverables.length; i++) {
if (window.CP.shouldStopExecution(0)) break;
$hoverables[i].addEventListener('mouseenter', onMouseHover);
$hoverables[i].addEventListener('mouseleave', onMouseHoverOut);
}
// Move the cursor
window.CP.exitedLoop(0); function onMouseMove(e) {
TweenMax.to($bigBall, .4, {
x: e.clientX - 15,
y: e.clientY - 15
});
TweenMax.to($smallBall, .1, {
x: e.clientX - 5,
y: e.clientY - 7
});
}
// Hover an element
function onMouseHover() {
TweenMax.to($bigBall, .3, {
scale: 4
});
}
function onMouseHoverOut() {
TweenMax.to($bigBall, .3, {
scale: 1
});
}
</script>```
Growing cursor on hovering a link.
The codepen you are trying to copy links to external scripts and has been adapted to work inside codepen.
What I did to get the pen to work here (I think you only needed step 4):
For CSS & JS clicked down arrow top right, view compiled
Copy over all code
Settings>JS>External Scripts>Copy link into Stack Overflow snippet's external scripts
Removed 'window.CP' code junk which I assume is something to do with codepen and getting the window object of their sub-document
const $bigBall = document.querySelector('.cursor__ball--big');
const $smallBall = document.querySelector('.cursor__ball--small');
const $hoverables = document.querySelectorAll('.hoverable');
// Listeners
document.body.addEventListener('mousemove', onMouseMove);
for (let i = 0; i < $hoverables.length; i++) {
$hoverables[i].addEventListener('mouseenter', onMouseHover);
$hoverables[i].addEventListener('mouseleave', onMouseHoverOut);
}
// Move the cursor
function onMouseMove(e) {
TweenMax.to($bigBall, .4, {
x: e.pageX - 15,
y: e.pageY - 15 });
TweenMax.to($smallBall, .1, {
x: e.pageX - 5,
y: e.pageY - 7 });
}
// Hover an element
function onMouseHover() {
TweenMax.to($bigBall, .3, {
scale: 4 });
}
function onMouseHoverOut() {
TweenMax.to($bigBall, .3, {
scale: 1 });
}
body {
height: 100vh;
background: #010101;
cursor: none;
margin: 0;
display: flex;
font-family: monospace;
}
body h1,
body p,
body a {
color: #fff;
}
body a {
border-bottom: 2px solid #fff;
padding: 10px 0;
margin-top: 25px;
}
body .cursor {
pointer-events: none;
}
body .cursor__ball {
position: fixed;
top: 0;
left: 0;
mix-blend-mode: difference;
z-index: 1000;
}
body .cursor__ball circle {
fill: #f7f8fa;
}
body .left,
body .right {
height: 100%;
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
body .right {
background: #fff;
}
body .right a {
border-bottom: 2px solid #000;
}
body .right h1,
body .right p,
body .right a {
color: #000;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.3/TweenMax.min.js"></script>
<div class="cursor">
<div class="cursor__ball cursor__ball--big ">
<svg height="30" width="30">
<circle cx="15" cy="15" r="12" stroke-width="0"></circle>
</svg>
</div>
<div class="cursor__ball cursor__ball--small">
<svg height="10" width="10">
<circle cx="5" cy="5" r="4" stroke-width="0"></circle>
</svg>
</div>
</div>
<div class="left">
<h1>Hello</h1>
<p>Check out this link:</p>
<a class="hoverable">Hover meh</a>
</div>
<div class="right">
<h1>Hello</h1>
<p>Check out this link:</p>
<a class="hoverable">Hover meh</a>
</div>
This question already has an answer here:
Cannot scale svg if created with js
(1 answer)
Closed 10 months ago.
I'm trying to add an svg dynamically to a menu, but I'm having a problem setting the viewbox.
When I inline the svgs (svg and svg-2), the viewbox has to be correct for the respective icon (0 0 512 512 and 0 0 24 24), as expected. Changing the viewbox removes the icon from view.
But for my dynamically added icons (dynamic-svg and dynamic-svg-2), changing the viewbox values (0 0 24 24 and 0 0 512 512) does nothing. In fact I can't get dynamic-svg-2 to show at all. dynamic-svg continues to display even if I change the viewbox to random values.
I must be doing something wrong, or have a bug somewhere, but I really can't see it. Would appreciate if someone could take a look. Thanks
codepen
const container = document.querySelector('.container');
const svgWrapper = document.createElement('div');
svgWrapper.className = 'dynamic-svg-wrapper'
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg")
const path1 = document.createElementNS("http://www.w3.org/2000/svg", 'path')
svg.setAttribute("aria-hidden","true");
svg.setAttribute('viewbox', '0 0 24 24');
svg.setAttribute('class', 'dynamic-svg');
path1.setAttribute('d', `M9 8h-3v4h3v12h5v-12h3.642l.358-4h-4v-1.667c0-.955.192-1.333 1.115-1.333h2.885v-5h-3.808c-3.596 0-5.192 1.583-5.192 4.615v3.385z`);
path1.setAttribute('fill', '#000000');
svg.append(path1);
svgWrapper.append(svg);
container.append(svgWrapper);
const svgWrapper2 = document.createElement('div');
svgWrapper2.className = 'dynamic-svg-wrapper-2'
const svg2 = document.createElementNS("http://www.w3.org/2000/svg", "svg")
const path2 = document.createElementNS("http://www.w3.org/2000/svg", 'path')
svg2.setAttribute("aria-hidden","true");
svg2.setAttribute('viewbox', '0 0 512 512');
svg2.setAttribute('class', 'dynamic-svg-2');
path2.setAttribute('d', `M461.6,109.6l-54.9-43.3c-1.7-1.4-3.8-2.4-6.2-2.4c-2.4,0-4.6,1-6.3,2.5L194.5,323c0,0-78.5-75.5-80.7-77.7
c-2.2-2.2-5.1-5.9-9.5-5.9c-4.4,0-6.4,3.1-8.7,5.4c-1.7,1.8-29.7,31.2-43.5,45.8c-0.8,0.9-1.3,1.4-2,2.1c-1.2,1.7-2,3.6-2,5.7
c0,2.2,0.8,4,2,5.7l2.8,2.6c0,0,139.3,133.8,141.6,136.1c2.3,2.3,5.1,5.2,9.2,5.2c4,0,7.3-4.3,9.2-6.2L462,121.8
c1.2-1.7,2-3.6,2-5.8C464,113.5,463,111.4,461.6,109.6z`);
path2.setAttribute('fill', '#000000');
svg2.append(path2);
svgWrapper2.append(svg2);
container.append(svgWrapper2);
* {
margin: 0;
}
.container {
display: flex;
justify-content: center;
width: 100%;
height: 100vh;
background-color: lightgrey;
}
.svg-wrapper {
display: flex;
width: 24px;
height: 24px;
border: 1px solid black;
margin: 10px;
}
.svg {
display: flex;
width: 24px;
height: 24px;
}
.svg-wrapper-2 {
display: flex;
width: 24px;
height: 24px;
border: 1px solid black;
margin: 10px;
}
.dynamic-svg-wrapper {
display: flex;
width: 24px;
height: 24px;
border: 1px solid black;
margin: 10px;
}
.dynamic-svg {
display: flex;
width: 24px;
height: 24px;
}
.dynamic-svg-wrapper-2 {
display: flex;
width: 24px;
height: 24px;
border: 1px solid black;
margin: 10px;
}
.dynamic-svg-2 {
display: flex;
width: 24px;
height: 24px;
}
<div class="container">
<div class="svg-wrapper">
<svg class="svg" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 512 512">
<path d="M461.6,109.6l-54.9-43.3c-1.7-1.4-3.8-2.4-6.2-2.4c-2.4,0-4.6,1-6.3,2.5L194.5,323c0,0-78.5-75.5-80.7-77.7
c-2.2-2.2-5.1-5.9-9.5-5.9c-4.4,0-6.4,3.1-8.7,5.4c-1.7,1.8-29.7,31.2-43.5,45.8c-0.8,0.9-1.3,1.4-2,2.1c-1.2,1.7-2,3.6-2,5.7
c0,2.2,0.8,4,2,5.7l2.8,2.6c0,0,139.3,133.8,141.6,136.1c2.3,2.3,5.1,5.2,9.2,5.2c4,0,7.3-4.3,9.2-6.2L462,121.8
c1.2-1.7,2-3.6,2-5.8C464,113.5,463,111.4,461.6,109.6z"/>
</svg>
</div>
<div class="svg-wrapper-2">
<svg class='svg-2' xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9 8h-3v4h3v12h5v-12h3.642l.358-4h-4v-1.667c0-.955.192-1.333 1.115-1.333h2.885v-5h-3.808c-3.596 0-5.192 1.583-5.192 4.615v3.385z"/></svg>
</div>
</div>
]1
You can save yourself a lot of headaches using modern technologies,
A <svg-icon> Web Component, supported in all modern browsers.
I processed some of your <path> with https://yqnn.github.io/svg-path-editor/, to make them smaller.
You don't need 0.nnn precision when you stuff a 512x512 viewBox in 40x40 pixels
It does not matter where or when you execute customElements.define;
all existing or new <svg-icon> will automagically upgrade.
<style>
div {
display: flex; justify-content: center;
width:100%; background:pink;
}
</style>
<div>
<svg-icon>
<path fill="green" d="m461.6 109.6-54.9-43.3c-1.7-1.4-3.8-2.4-6.2-2.4-2.4 0-4.6 1-6.3 2.5l-199.7 256.6c0 0-78.5-75.5-80.7-77.7-2.2-2.2-5.1-5.9-9.5-5.9-4.4 0-6.4 3.1-8.7 5.4-1.7 1.8-29.7 31.2-43.5 45.8-.8.9-1.3 1.4-2 2.1-1.2 1.7-2 3.6-2 5.7 0 2.2.8 4 2 5.7l2.8 2.6c0 0 139.3 133.8 141.6 136.1 2.3 2.3 5.1 5.2 9.2 5.2 4 0 7.3-4.3 9.2-6.2l249.1-320c1.2-1.7 2-3.6 2-5.8 0-2.5-1-4.6-2.4-6.4z" />
</svg-icon>
<svg-icon vb="5120">
<path fill="red" d="m4616 1096-549-433c-17-14-38-24-62-24-24 0-46 10-63 25l-1997 2566c0 0-785-755-807-777-22-22-51-59-95-59-44 0-64 31-87 54-17 18-297 312-435 458-8 9-13 14-20 21-12 17-20 36-20 57 0 22 8 40 20 57l28 26c0 0 1393 1338 1416 1361 23 23 51 52 92 52 40 0 73-43 92-62l2491-3200c12-17 20-36 20-58 0-25-10-46-24-64z" />
</svg-icon>
<svg-icon vb="24">
<path fill="blue" d="M9 8h-3v4h3v12h5v-12h3.642l.358-4h-4v-1.667c0-.955.192-1.333 1.115-1.333h2.885v-5h-3.808c-3.596 0-5.192 1.583-5.192 4.615v3.385z" />
</svg-icon>
<svg-icon vb="24">
<path fill="rebeccapurple" d="M9 8h-3v4h3v12h5v-12h3.642l.358-4h-4v-1.667c0-.955.192-1.333 1.115-1.333h2.885v-5h-3.808c-3.596 0-5.192 1.583-5.192 4.615v3.385z" />
</svg-icon>
</div>
<script>
customElements.define("svg-icon", class extends HTMLElement {
connectedCallback() {
let vb = this.getAttribute("vb") || 512;
setTimeout(() => { // wait till path innerHTML is parsed
let svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.setAttribute("viewBox",`0 0 ${vb} ${vb}`);
svg.innerHTML = this.innerHTML;
this.replaceWith(svg); // Web Component did its job, no need to keep it
});
}
});
</script>
Take this idea some steps further and you get https://iconmeister.github.io
I want to avoid duplicate drop on mxgraph canvas.
let say I have dragged Pipe on canvas 2nd time it should not allow it be dragged on canvas.
Question: how to avoid duplicate drop on canvas
Here is my working code drag with duplicate allowed
Drag and Drop
var graph = {};
function initCanvas() {
//This function is called onload of body itself and it will make the mxgraph canvas
graph = new mxGraph(document.getElementById('graph-wrapper'));
graph.htmlLabels = true;
graph.cellsEditable = false;
// render as HTML node always. You probably won't want that in real world though
graph.convertValueToString = function(cell) {
return cell.value;
}
const createDropHandler = function (cells, allowSplit) {
return function (graph, evt, target, x, y) {
const select = graph.importCells(cells, x, y, target);
graph.setSelectionCells(select);
};
};
const createDragPreview = function (width, height) {
var elt = document.createElement('div');
elt.style.border = '1px dashed black';
elt.style.width = width + 'px';
elt.style.height = height + 'px';
return elt;
};
const createDragSource = function (elt, dropHandler, preview) {
return mxUtils.makeDraggable(elt, graph, dropHandler, preview, 0, 0, graph.autoscroll, true, true);
};
const createItem = (id) => {
const elt = document.getElementById(id);
const width = elt.clientWidth;
const height = elt.clientHeight;
const cell = new mxCell('', new mxGeometry(0, 0, width, height), 'fillColor=none;strokeColor=none');
cell.vertex = true;
graph.model.setValue(cell, elt);
const cells = [cell];
const bounds = new mxRectangle(0, 0, width, height);
createDragSource(elt, createDropHandler(cells, true, false, bounds), createDragPreview(width, height), cells, bounds);
};
createItem("shape_1");
createItem("shape_2");
createItem("shape_3");
}
#graph-wrapper {
background: #333;
width: 100%;
height: 528px;
}
<html>
<head>
<title>Toolbar example for mxGraph</title>
<script type="text/javascript">
mxBasePath = 'https://jgraph.github.io/mxgraph/javascript/src';
</script>
<script src="https://jgraph.github.io/mxgraph/javascript/src/js/mxClient.js"></script>
<script src="./app.js"></script>
</head>
<body onload="initCanvas()">
<h4>Drag same box 2 times on the canvas. see duplicate is allowed</h4>
<div>
<div id="shape_1"
style="width: 100px; height: 100px; border-radius: 50%; background: red; display: inline-flex; text-align: center; color: #fff; align-items: center; justify-content: center;">
Pipe
</div>
<div draggable="true" id="shape_2"
style="width: 100px; height: 100px; border-radius: 5%; background: orange; display: inline-flex; text-align: center; color: #fff; align-items: center; justify-content: center;">
Team
</div>
<div draggable="true" id="shape_3"
style="width: 100px; height: 64px; background: #009688; display: inline-flex; text-align: center; color: #fff; align-items: center; justify-content: center; border-radius: 207px; flex-direction: column;">
<div> <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000">
<path d="M0 0h24v24H0V0z" fill="none" />
<path
d="M11 7h2v2h-2zm0 4h2v6h-2zm1-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z" />
</svg></div>
<div>Info</div>
</div>
</div>
<div id="graph-wrapper">
</div>
</body>
</html>
You just need check all current cells value of maxgarph in createDropHandler. So get all cells by graph.getModel().cells and then check to see if the cells you want to add exists or not:
let allcells = graph.getModel().cells;
for (var i in allcells)
if (allcells[i].value && (allcells[i].value.id == cells[0].value.id))
return;
var graph = {};
function initCanvas() {
//This function is called onload of body itself and it will make the mxgraph canvas
graph = new mxGraph(document.getElementById('graph-wrapper'));
graph.htmlLabels = true;
graph.cellsEditable = false;
// render as HTML node always. You probably won't want that in real world though
graph.convertValueToString = function (cell) {
return cell.value;
}
const createDropHandler = function (cells, allowSplit) {
return function (graph, evt, target, x, y) {
debugger
let allcells = graph.getModel().cells;
for (var i in allcells)
if (allcells[i].value && (allcells[i].value.id == cells[0].value.id))
return;
const select = graph.importCells(cells, x, y, target);
graph.setSelectionCells(select);
};
};
const createDragPreview = function (width, height) {
var elt = document.createElement('div');
elt.style.border = '1px dashed black';
elt.style.width = width + 'px';
elt.style.height = height + 'px';
return elt;
};
const createDragSource = function (elt, dropHandler, preview) {
return mxUtils.makeDraggable(elt, graph, dropHandler, preview, 0, 0, graph.autoscroll, true, true);
};
const createItem = (id) => {
const elt = document.getElementById(id);
const width = elt.clientWidth;
const height = elt.clientHeight;
const cell = new mxCell('', new mxGeometry(0, 0, width, height), 'fillColor=none;strokeColor=none');
cell.vertex = true;
graph.model.setValue(cell, elt);
const cells = [cell];
const bounds = new mxRectangle(0, 0, width, height);
createDragSource(elt, createDropHandler(cells, true, false, bounds), createDragPreview(width, height), cells, bounds);
};
createItem("shape_1");
createItem("shape_2");
createItem("shape_3");
}
<html>
<head>
<title>Toolbar example for mxGraph</title>
<script type="text/javascript">
mxBasePath = 'https://jgraph.github.io/mxgraph/javascript/src';
</script>
<script src="https://jgraph.github.io/mxgraph/javascript/src/js/mxClient.js"></script>
<script src="./app.js"></script>
<style>
#graph-wrapper {
background: #333;
width: 100%;
height: 528px;
}
</style>
</head>
<body onload="initCanvas()">
<h4>Drag same box 2 times on the canvas. see duplicate is allowed</h4>
<div>
<div id="shape_1"
style="width: 100px; height: 100px; border-radius: 50%; background: red; display: inline-flex; text-align: center; color: #fff; align-items: center; justify-content: center;">
Pipe
</div>
<div draggable="true" id="shape_2"
style="width: 100px; height: 100px; border-radius: 5%; background: orange; display: inline-flex; text-align: center; color: #fff; align-items: center; justify-content: center;">
Team
</div>
<div draggable="true" id="shape_3"
style="width: 100px; height: 64px; background: #009688; display: inline-flex; text-align: center; color: #fff; align-items: center; justify-content: center; border-radius: 207px; flex-direction: column;">
<div>
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000">
<path d="M0 0h24v24H0V0z" fill="none" />
<path d="M11 7h2v2h-2zm0 4h2v6h-2zm1-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z" />
</svg>
</div>
<div>Info</div>
</div>
</div>
<div id="graph-wrapper">
</div>
First of all, I don't have any idea about mxgraph. To solve your problem I made some research on documentation of the library and I didn't find any tips.
I used the basics of the Javascript to achieve this and I didn't touch any of your main code and flow. I just add the draggable_status to graph element on createDropHandler function.
When you drag once, the below code add a status to the object and you can't drag anymore just adding a simple control.
graph.draggable_status = false;
To control, should be in the same function - const createDropHandler = function (cells, allowSplit)
if(graph.draggable_status != false){
const select = graph.importCells(cells, x, y, target);
graph.setSelectionCells(select);
graph.draggable_status = false;
}
The full code snippet is here;
var graph = {};
function initCanvas() {
//This function is called onload of body itself and it will make the mxgraph canvas
graph = new mxGraph(document.getElementById('graph-wrapper'));
graph.htmlLabels = true;
graph.cellsEditable = false;
// render as HTML node always. You probably won't want that in real world though
graph.convertValueToString = function(cell) {
return cell.value;
}
const createDropHandler = function (cells, allowSplit) {
return function (graph, evt, target, x, y) {
if(graph.draggable_status != false){
const select = graph.importCells(cells, x, y, target);
graph.setSelectionCells(select);
graph.draggable_status = false;
}
};
};
const createDragPreview = function (width, height) {
var elt = document.createElement('div');
elt.style.border = '1px dashed black';
elt.style.width = width + 'px';
elt.style.height = height + 'px';
return elt;
};
const createDragSource = function (elt, dropHandler, preview) {
return mxUtils.makeDraggable(elt, graph, dropHandler, preview, 0, 0, graph.autoscroll, true, true);
};
const createItem = (id) => {
const elt = document.getElementById(id);
const width = elt.clientWidth;
const height = elt.clientHeight;
const cell = new mxCell('', new mxGeometry(0, 0, width, height), 'fillColor=none;strokeColor=none');
cell.vertex = true;
graph.model.setValue(cell, elt);
const cells = [cell];
const bounds = new mxRectangle(0, 0, width, height);
createDragSource(elt, createDropHandler(cells, true, false, bounds), createDragPreview(width, height), cells, bounds);
};
createItem("shape_1");
createItem("shape_2");
createItem("shape_3");
}
#graph-wrapper {
background: #333;
width: 100%;
height: 528px;
}
<html>
<head>
<title>Toolbar example for mxGraph</title>
<script type="text/javascript">
mxBasePath = 'https://jgraph.github.io/mxgraph/javascript/src';
</script>
<script src="https://jgraph.github.io/mxgraph/javascript/src/js/mxClient.js"></script>
<script src="./app.js"></script>
</head>
<body onload="initCanvas()">
<h4>Drag same box 2 times on the canvas. see duplicate is allowed</h4>
<div>
<div id="shape_1"
style="width: 100px; height: 100px; border-radius: 50%; background: red; display: inline-flex; text-align: center; color: #fff; align-items: center; justify-content: center;">
Pipe
</div>
<div draggable="true" id="shape_2"
style="width: 100px; height: 100px; border-radius: 5%; background: orange; display: inline-flex; text-align: center; color: #fff; align-items: center; justify-content: center;">
Team
</div>
<div draggable="true" id="shape_3"
style="width: 100px; height: 64px; background: #009688; display: inline-flex; text-align: center; color: #fff; align-items: center; justify-content: center; border-radius: 207px; flex-direction: column;">
<div> <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000">
<path d="M0 0h24v24H0V0z" fill="none" />
<path
d="M11 7h2v2h-2zm0 4h2v6h-2zm1-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z" />
</svg></div>
<div>Info</div>
</div>
</div>
<div id="graph-wrapper">
</div>
</body>
</html>
I have a VueJS component which is a svg map image of the united states. I previously used jquery to create hover effects to display the information bubble. I am trying to convert this to a pure Javascript solution. It seems a click event is the easiest to implement given vuejs mousemove seems to be less reliable. I have added the click event in method and I am trying to capture the SVG path location and steal the top and left position to enable the info box there. I have tried this.offsettop and this.offsetleft but they return undefined.
Jquery code I am trying to convert:
$("path, circle").hover(function(e) {
$('#info-box').css('display','block');
$('#info-box').html($(this).data('info'));
});
$("path, circle").mouseleave(function(e) {
$('#info-box').css('display','none');
});
$(document).mousemove(function(e) {
$('#info-box').css('top',e.pageY-$('#info-box').height()-30);
$('#info-box').css('left',e.pageX-($('#info-box').width())/2);
}).mouseover();
var ios = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
if(ios) {
$('a').on('click touchend', function() {
var link = $(this).attr('href');
window.open(link,'_blank');
return false;
});
}
VueJS Component
<template id="geomap-assignments">
<v-card class="pa-4 ma-3 rounded-lg" min-height="500px">
<div class="card-title-docs">
<h4 class="card-header">Assignments</h4>
<h5 class="card-sub-header"></h5>
</div>
<div class="map-container">
<div id="info-box"></div>
<?xml version="1.0" encoding="utf-8" ?>
<svg xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="us-map" preserveAspectRatio="xMinYMin meet" sodipodi:docname="Republican_Party_presidential_primaries_results,_2016.svg" inkscape:version="0.91 r13725" x="0px" y="0px" width="959px" height="593px" viewBox="174 100 959 593" enable-background="new 174 100 959 593" xml:space="preserve">
<sodipodi:namedview bordercolor="#666666" objecttolerance="10" pagecolor="#ffffff" borderopacity="1" gridtolerance="10" guidetolerance="10" inkscape:cx="509.19152" inkscape:cy="282.2353" inkscape:zoom="1.2137643" showgrid="false" id="namedview71" inkscape:current-layer="g5" inkscape:window-maximized="1" inkscape:window-y="-8" inkscape:window-x="-8" inkscape:pageopacity="0" inkscape:window-height="1017" inkscape:window-width="1920" inkscape:pageshadow="2">
</sodipodi:namedview>
<g id="g5">
<path id="HI" data-info="<div>State: Hawaii</div><div>Capital: Honolulu</div>" fill="#D3D3D3" d="M407.1,619.3l1.9-3.6l2.3-0.3l0.3,0.8l-2.1,3.1H407.1z M417.3,615.6l6.1,2.6l2.1-0.3l1.6-3.9 l-0.6-3.4l-4.2-0.5l-4,1.8L417.3,615.6z M448,625.6l3.7,5.5l2.4-0.3l1.1-0.5l1.5,1.3l3.7-0.2l1-1.5l-2.9-1.8l-1.9-3.7l-2.1-3.6 l-5.8,2.9L448,625.6z M468.2,634.5l1.3-1.9l4.7,1l0.6-0.5l6.1,0.6l-0.3,1.3l-2.6,1.5l-4.4-0.3L468.2,634.5z M473.5,639.7l1.9,3.9 l3.1-1.1l0.3-1.6l-1.6-2.1l-3.7-0.3V639.7z M480.5,638.5l2.3-2.9l4.7,2.4l4.4,1.1l4.4,2.7v1.9l-3.6,1.8l-4.8,1l-2.4-1.5 L480.5,638.5z M497.1,654.1l1.6-1.3l3.4,1.6l7.6,3.6l3.4,2.1l1.6,2.4l1.9,4.4l4,2.6l-0.3,1.3l-3.9,3.2l-4.2,1.5l-1.5-0.6l-3.1,1.8 l-2.4,3.2l-2.3,2.9l-1.8-0.2l-3.6-2.6l-0.3-4.5l0.6-2.4l-1.6-5.7l-2.1-1.8l-0.2-2.6l2.3-1l2.1-3.1l0.5-1l-1.6-1.8L497.1,654.1z" />
<path id="OK" data-info="<div>State: Oklahoma</div><div>Capital: Oklahoma City</div>" fill="#D3D3D3" d="M549.3,422.6l-10.7-0.5l-6.4-0.5l0.3,0.2l-0.7,10.4l22,1.4l32.1,1.3l-2.3,24.4l-0.5,17.8l0.2,1.6 l4.3,3.7l2.1,1.1l0.7-0.2l0.7-2.1l1.4,1.8h2.1v-1.4l2.7,1.4l-0.5,3.9l4.1,0.2l2.5,1.1l4.1,0.7l2.5,1.8l2.3-2.1l3.4,0.7l2.5,3.4h0.9 v2.3l2.3,0.7l2.3-2.3l1.8,0.7h2.5l0.9,2.5l4.8,1.8l1.4-0.7l1.8-4.1h1.1l1.1,2.1l4.1,0.7l3.7,1.4l3,0.9l1.8-0.9l0.7-2.5h4.3l2.1,0.9 l2.7-2.1h1.1l0.7,1.6h4.1l1.6-2.1l1.8,0.5l2.1,2.5l3.2,1.8l3.2,0.9l1.9,1.1l-0.4-37.2l-1.4-11l-0.2-8.9l-1.4-6.5l-0.8-7.2l-0.1-3.8 l-12.1,0.3l-46.4-0.5l-45-2.1L549.3,422.6z" />
<path id="KS" data-info="<div>State: Kansas</div><div>Capital: Topeka</div>" fill="#D3D3D3" d="M677.4,425.1l-12.6,0.2l-46.1-0.5l-44.6-2.1l-24.6-1.3l4.1-64.7l21.8,0.8l40.5,1.4l44.1,0.5h5.1 l3.2,3.2l2.8,0.2l0.9,1.1v2l-1.8,1.6l-0.5,2.6l2.2,3.6l2.5,3.1l2.5,2l1.1,11.2L677.4,425.1z" />
<path id="LA" data-info="<div>State: Louisiana</div><div>Capital: Baton Rouge</div>" fill="#D3D3D3" d="M776.2,573l-1-2.6l-1.1-3.1l-3.3-3.5l0.9-6.8l-0.1-1.1l-1.3,0.3l-8.2,0.9l-25,0.5l-0.7-2.4l0.9-8.5 l3.3-5.9l5-8.7l-0.6-2.4l1.3-0.7l0.5-2l-2.3-2.1l-0.1-1.9l-1.8-4.3l-0.5-5.9l-9.7,0.1l-19.2,0.9l-22.2,0l0,9.6l0.7,9.4l0.7,3.9 l2.5,4.1l0.9,5l4.3,5.5l0.2,3.2l0.7,0.7l-0.7,8.5l-3,5l1.6,2.1l-0.7,2.5l-0.7,7.3l-1.4,3.2l0.1,3.6l4.7-1.5l8.1-0.3l10.3,3.6 l6.5,1.1l3.7-1.5l3.2,1.1l3.2,1l0.8-2.1l-3.2-1.1l-2.6,0.5l-2.7-1.6c0,0,0.2-1.3,0.8-1.5c0.6-0.2,3.1-1,3.1-1l1.8,1.5l1.8-1 l3.2,0.6l1.5,2.4l0.3,2.3l4.5,0.3l1.8,1.8l-0.8,1.6l-1.3,0.8l1.6,1.6l8.4,3.6l3.6-1.3l1-2.4l2.6-0.6l1.8-1.5l1.3,1l0.8,2.9 l-2.3,0.8l0.6,0.6l3.4-1.3l2.3-3.4l0.8-0.5l-2.1-0.3l0.8-1.6l-0.2-1.5l2.1-0.5l1.1-1.3l0.6,0.8c0,0-0.2,3.1,0.6,3.1 c0.8,0,4.2,0.6,4.2,0.6l4,1.9l1,1.5h2.9l1.1,1l2.3-3.1v-1.5h-1.3l-3.4-2.7l-5.8-0.8l-3.2-2.3l1.1-2.7l2.3,0.3l0.2-0.6l-1.8-1v-0.5 h3.2l1.8-3.1l-1.3-1.9l-0.3-2.7l-1.5,0.2l-1.9,2.1l-0.6,2.6l-3.1-0.6l-1-1.8l1.8-1.9l2-1.8L776.2,573z" />
<path id="VA" data-info="<div>State: Virginia</div><div>Capital: Richmond</div>" fill="#D3D3D3" d="M1002.9,369.2l-0.1-1.9l6.5-2.5l-0.8,3.2l-2.9,3.8l-0.4,4.6l0.5,3.4l-1.8,5l-2.2,1.9l-1.5-4.6 l0.4-5.4l1.6-4.2L1002.9,369.2z M1005.2,397.5L947,410.1l-37.4,5.3l-6.7-0.4l-2.6,1.9l-7.3,0.2l-8.4,1l-8.9,1l8.5-4.9l0-2.1 l1.5-2.1l10.6-11.5l3.9,4.5l3.8,1l2.5-1.1l2.2-1.3l2.5,1.3l3.9-1.4l1.9-4.6l2.6,0.5l2.9-2.1l1.8,0.5l2.8-3.7l0.3-2.1l-1-1.3l1-1.9 l5.3-12.3l0.6-5.7l1.2-0.5l2.2,2.4l3.9-0.3l1.9-7.6l2.8-0.6l1-2.7l2.6-2.3l1.3-2.3l1.5-3.4l0.1-5.1l9.8,3.8 c0.7,0.3,0.7-4.8,0.7-4.8l4.1,1.4l-0.5,2.6l8.2,2.9l1.3,1.8l-0.9,3.7l-1.3,1.3l-0.5,1.7l0.5,2.4l2,1.3l3.9,1.4l2.9,1l4.9,0.9 l2.2,2.1l3.2,0.4l0.9,1.2l-0.4,4.7l1.4,1.1l-0.5,1.9l1.2,0.8l-0.2,1.4l-2.7-0.1l0.1,1.6l2.3,1.5l0.1,1.4l1.8,1.8l0.5,2.5l-2.6,1.4 l1.6,1.5l5.8-1.7L1005.2,397.5z" />
<g id="DC">
<path id="path58" fill="#D3D3D3" d="M975.8,353.8l-1.1-1.6l-1-0.8l1.1-1.6l2.2,1.5L975.8,353.8z" />
<circle id="circle60" data-info="<div>Washington DC</div>" fill="#D3D3D3" stroke="#FFFFFF" stroke-width="1.5" cx="975.3" cy="351.8" r="5" />
</g>
</g>
<path id="path67" fill="none" stroke="#A9A9A9" stroke-width="2" d="M385,593v55l36,45 M174,525h144l67,68h86l53,54v46" />
</svg>
</div>
</v-card>
</template>
<script>
Vue.component('geomap-assignments', {
template: '#geomap-assignments',
methods: {
activateCaption: function (event) {
// `event` is the native DOM event
if (event) {
const left = this.offsetTop
const top = this.offsetTop
alert("LEFT" + left + "RIGHT" + top);
}
}
},
data() {
return {
documents: [
]
}
}
})
</script>
<style scoped>
.card-title-docs {
margin: 20px 20px;
}
.card-header {
font-size: 1rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 1px;
color: #515151;
}
.card-sub-header {
font-size: .8rem;
font-weight: 500;
text-transform: uppercase;
letter-spacing: .7px;
color: #7f818d;
}
.map-container{
position:relative;
width: 100%;
height: 100%;
}
#us-map {
display: block;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
path:hover, circle:hover {
stroke: #002868 !important;
stroke-width: 2px;
stroke-linejoin: round;
fill: #002868 !important;
cursor: pointer;
}
#path67 {
fill: none !important;
stroke: #A9A9A9 !important;
cursor: default;
}
#info-box {
display: none;
position: absolute;
top: 0px;
left: 0px;
z-index: 1;
background-color: #ffffff;
border: 2px solid #BF0A30;
border-radius: 5px;
padding: 5px;
font-family: arial;
}
</style>
'''
The solution was found in the comments on the question.
In summary, the issue was understanding how to position the #info-box element. Using the getBoundingClientRect() function, we can get the position of any element relative to the entire document. To get the position of the clicked path element, simply subtract the top and left of the #us-map from the top and left of the path. This will give the position for the top left corner of the path, relative to the containing element. We can then use that to position the #info-box.