javascript - D3 Context menu with sub-menu - Stack Overflow

admin2025-04-22  0

I am creating context menu based on javascript array with callback functions but getting two issues

  1. unable to hide submenu when mouse moves to an element with no children nodes(On Delete Section).

  2. 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

  1. unable to hide submenu when mouse moves to an element with no children nodes(On Delete Section).

  2. 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?

Share Improve this question asked Nov 25, 2015 at 10:11 Abdul RehmanAbdul Rehman 4155 silver badges16 bronze badges
Add a ment  | 

1 Answer 1

Reset to default 7

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.

转载请注明原文地址:http://conceptsofalgorithm.com/Algorithm/1745276708a293914.html

最新回复(0)