I am creating context menu based on javascript array with callback functions but getting two issues
unable to hide submenu when mouse moves to an element with no children nodes(On Delete Section).
unable to show submenus in proper hierarchy(After moving to Sibling Section unable to go back to submenu of child section).
My code:
d3.contextMenu = function (menu, openCallback) {
// create the div element that will hold the context menu
d3.selectAll('.d3-context-menu').data([1])
.enter()
.append('div')
.attr('class', 'd3-context-menu');
// close menu
d3.select('body').on('click.d3-context-menu', function() {
d3.select('.d3-context-menu').style('display', 'none');
});
// this gets executed when a contextmenu event occurs
return function(data, index) {
var elm = this;
d3.selectAll('.d3-context-menu').html('');
var list = d3.selectAll('.d3-context-menu').append('ul');
list.selectAll('li').data(menu).enter()
.append('li')
.html(function(d) {
return d.title;
})
.on('click', function(d, i) {
d.onMouseClick(elm, data, index);
})
.on('mouseover',function(d,i){
d.onMouseOver(elm,data,index);
if(d.chidernItems.length>0 )
{
d3.select(this).selectAll("ul").remove();
d3.select(this)
.append("ul")
.selectAll("li")
.data(d.chidernItems)
.enter().append("li")
.text(function(d) { return d.title; })
.on("mouseover", function(d,i){
d.onMouseOver(elm,data,index);
})
.on('click', function(d, i) {
d.onMouseClick(elm, data, index);
})
.on('mouseout',function(d,i){
d3.select(this).remove();
});
}
else
return false;
})
.on('mouseout',function(d,i){
if(d.chidernItems.length==0 )
{
d3.select(this).selectAll("ul").remove();
}
});
// the openCallback allows an action to fire before the menu is displayed
// an example usage would be closing a tooltip
if (openCallback) openCallback(data, index);
// display context menu
d3.select('.d3-context-menu')
.style('left', (d3.event.pageX - 2) + 'px')
.style('top', (d3.event.pageY - 2) + 'px')
.style('display', 'block');
d3.event.preventDefault();
};
};
var menu = [
{
title: 'Create Child Section',
// Exceute Action
onMouseClick: function(elm, d, i) {
console.log('Create Child Section clicked!');
console.log('The data for this circle is: ' + d);
},
onMouseOver: function(elm,d,i){
console.log('Create Child mouseover data = ' +d );
},
chidernItems: [
{
title: 'Vertical',
// Exceute Action
onMouseClick: function(elm, d, i) {
console.log('Vertical Create Child clicked!');
console.log('The data for this circle is: ' + d);
},
onMouseOver: function(elm,d,i){
console.log('Vertical Create Child mouseover data = ' +d );
}
},
{
title: 'Horizontal',
// Exceute Action
onMouseClick: function(elm, d, i) {
console.log('Horizontal Create Child clicked!');
console.log('The data for this circle is: ' + d);
},
onMouseOver: function(elm,d,i){
console.log('Horizontal Create Child mouseover data = ' +d );
}
}
]
},
{
title: 'Create Sibling Section',
// Exceute Action
onMouseClick: function(elm, d, i) {
console.log('Create Sibling Section clicked!');
console.log('The data for this circle is: ' + d);
},
onMouseOver: function(elm,d,i){
console.log('Sibling Section mouseover data = ' +d );
},
chidernItems: [
{
title: 'Vertical',
// Exceute Action
onMouseClick: function(elm, d, i) {
console.log('Sibling Vertical clicked!');
console.log('The data for this circle is: ' + d);
},
onMouseOver: function(elm,d,i){
console.log('Sibling Vertical mouseover data = ' +d );
}
},
{
title: 'Horizontal',
// Exceute Action
onMouseClick: function(elm, d, i) {
console.log('Horizontal clicked!');
console.log('The data for this circle is: ' + d);
},
onMouseOver: function(elm,d,i){
console.log('Horizontal mouseover data = ' +d );
}
}
]
},
{
title: 'Delete Section',
// Exceute Action
onMouseClick: function(elm, d, i) {
console.log('Delete Section Section clicked!');
console.log('The data for this circle is: ' + d);
},
onMouseOver: function(elm,d,i){
console.log('Delete Section mouseover data = ' +d );
},
chidernItems: []
}]
var data = [1];
var g = d3.select('body').append('svg')
.attr('width', 200)
.attr('height', 400)
.append('g');
g.selectAll('circles')
.data(data)
.enter()
.append('circle')
.attr('r', 30)
.attr('fill', 'steelblue')
.attr('cx', function(d) {
return 100;
})
.attr('cy', function(d) {
return d * 100;
})
.on('contextmenu', d3.contextMenu(menu));
.d3-context-menu {
position: absolute;
display: none;
background-color: #f2f2f2;
border-radius: 4px;
font-family: Arial, sans-serif;
font-size: 14px;
min-width: 150px;
border: 1px solid #d4d4d4;
z-index:1200;
}
.d3-context-menu ul {
list-style-type: none;
margin: 4px 0px;
padding: 0px;
cursor: default;
}
.d3-context-menu ul li {
padding: 4px 16px;
}
.d3-context-menu ul li:hover {
background-color: #4677f8;
color: #fefefe;
}
.d3-context-menu ul li > ul {
position: absolute;
background-color: #f2f2f2;
top: 0;
left: 175px;
z-index: -1;
}
.d3-context-menu ul li > ul li:hover
{
background-color: #4677f8;
color: #fefefe;
}
<script src=".4.0/d3.min.js"></script>
I am creating context menu based on javascript array with callback functions but getting two issues
unable to hide submenu when mouse moves to an element with no children nodes(On Delete Section).
unable to show submenus in proper hierarchy(After moving to Sibling Section unable to go back to submenu of child section).
My code:
d3.contextMenu = function (menu, openCallback) {
// create the div element that will hold the context menu
d3.selectAll('.d3-context-menu').data([1])
.enter()
.append('div')
.attr('class', 'd3-context-menu');
// close menu
d3.select('body').on('click.d3-context-menu', function() {
d3.select('.d3-context-menu').style('display', 'none');
});
// this gets executed when a contextmenu event occurs
return function(data, index) {
var elm = this;
d3.selectAll('.d3-context-menu').html('');
var list = d3.selectAll('.d3-context-menu').append('ul');
list.selectAll('li').data(menu).enter()
.append('li')
.html(function(d) {
return d.title;
})
.on('click', function(d, i) {
d.onMouseClick(elm, data, index);
})
.on('mouseover',function(d,i){
d.onMouseOver(elm,data,index);
if(d.chidernItems.length>0 )
{
d3.select(this).selectAll("ul").remove();
d3.select(this)
.append("ul")
.selectAll("li")
.data(d.chidernItems)
.enter().append("li")
.text(function(d) { return d.title; })
.on("mouseover", function(d,i){
d.onMouseOver(elm,data,index);
})
.on('click', function(d, i) {
d.onMouseClick(elm, data, index);
})
.on('mouseout',function(d,i){
d3.select(this).remove();
});
}
else
return false;
})
.on('mouseout',function(d,i){
if(d.chidernItems.length==0 )
{
d3.select(this).selectAll("ul").remove();
}
});
// the openCallback allows an action to fire before the menu is displayed
// an example usage would be closing a tooltip
if (openCallback) openCallback(data, index);
// display context menu
d3.select('.d3-context-menu')
.style('left', (d3.event.pageX - 2) + 'px')
.style('top', (d3.event.pageY - 2) + 'px')
.style('display', 'block');
d3.event.preventDefault();
};
};
var menu = [
{
title: 'Create Child Section',
// Exceute Action
onMouseClick: function(elm, d, i) {
console.log('Create Child Section clicked!');
console.log('The data for this circle is: ' + d);
},
onMouseOver: function(elm,d,i){
console.log('Create Child mouseover data = ' +d );
},
chidernItems: [
{
title: 'Vertical',
// Exceute Action
onMouseClick: function(elm, d, i) {
console.log('Vertical Create Child clicked!');
console.log('The data for this circle is: ' + d);
},
onMouseOver: function(elm,d,i){
console.log('Vertical Create Child mouseover data = ' +d );
}
},
{
title: 'Horizontal',
// Exceute Action
onMouseClick: function(elm, d, i) {
console.log('Horizontal Create Child clicked!');
console.log('The data for this circle is: ' + d);
},
onMouseOver: function(elm,d,i){
console.log('Horizontal Create Child mouseover data = ' +d );
}
}
]
},
{
title: 'Create Sibling Section',
// Exceute Action
onMouseClick: function(elm, d, i) {
console.log('Create Sibling Section clicked!');
console.log('The data for this circle is: ' + d);
},
onMouseOver: function(elm,d,i){
console.log('Sibling Section mouseover data = ' +d );
},
chidernItems: [
{
title: 'Vertical',
// Exceute Action
onMouseClick: function(elm, d, i) {
console.log('Sibling Vertical clicked!');
console.log('The data for this circle is: ' + d);
},
onMouseOver: function(elm,d,i){
console.log('Sibling Vertical mouseover data = ' +d );
}
},
{
title: 'Horizontal',
// Exceute Action
onMouseClick: function(elm, d, i) {
console.log('Horizontal clicked!');
console.log('The data for this circle is: ' + d);
},
onMouseOver: function(elm,d,i){
console.log('Horizontal mouseover data = ' +d );
}
}
]
},
{
title: 'Delete Section',
// Exceute Action
onMouseClick: function(elm, d, i) {
console.log('Delete Section Section clicked!');
console.log('The data for this circle is: ' + d);
},
onMouseOver: function(elm,d,i){
console.log('Delete Section mouseover data = ' +d );
},
chidernItems: []
}]
var data = [1];
var g = d3.select('body').append('svg')
.attr('width', 200)
.attr('height', 400)
.append('g');
g.selectAll('circles')
.data(data)
.enter()
.append('circle')
.attr('r', 30)
.attr('fill', 'steelblue')
.attr('cx', function(d) {
return 100;
})
.attr('cy', function(d) {
return d * 100;
})
.on('contextmenu', d3.contextMenu(menu));
.d3-context-menu {
position: absolute;
display: none;
background-color: #f2f2f2;
border-radius: 4px;
font-family: Arial, sans-serif;
font-size: 14px;
min-width: 150px;
border: 1px solid #d4d4d4;
z-index:1200;
}
.d3-context-menu ul {
list-style-type: none;
margin: 4px 0px;
padding: 0px;
cursor: default;
}
.d3-context-menu ul li {
padding: 4px 16px;
}
.d3-context-menu ul li:hover {
background-color: #4677f8;
color: #fefefe;
}
.d3-context-menu ul li > ul {
position: absolute;
background-color: #f2f2f2;
top: 0;
left: 175px;
z-index: -1;
}
.d3-context-menu ul li > ul li:hover
{
background-color: #4677f8;
color: #fefefe;
}
<script src="https://cdnjs.cloudflare./ajax/libs/d3/3.4.0/d3.min.js"></script>
I am deleting and creating submenu every time in mouseover function is there any better way?
fix the issue by changing 'mouseover'
and 'mouseout'
functions by 'mouseenter'
and 'mouseleave'
and introduce d3.select(this).selectAll("ul").style('display', 'none');
in mouseleave function.