You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Shiny的jsTreeR菜单列表项添加悬停文本弹窗?

实现jsTreeR菜单项悬停弹窗的方案

可以实现这个需求,核心思路是给Menu区域的可拖拽节点添加自定义tooltip数据,通过jQuery监听鼠标悬停事件来显示弹窗,同时确保复制到目标区域的节点不会保留该功能。具体实现如下:

1. 给可拖拽节点添加tooltip说明数据

在定义nodes时,为每个moveable类型的节点添加data属性,存储对应的悬停说明文本:

list(text = "Dog", type = "moveable", state = list(disabled = TRUE), data = list(tooltip = "一种常见的家养宠物,忠诚活泼")),
list(text = "Cat", type = "moveable", state = list(disabled = TRUE), data = list(tooltip = "温顺独立的家养宠物,喜欢独处"))

2. 添加tooltip样式与交互逻辑

在原有的JS脚本中新增tooltip的创建、悬停监听逻辑,同时在节点复制后清除目标节点的tooltip相关属性:

  • 创建全局tooltip元素,默认隐藏
  • 仅对Menu区域内的moveable节点触发悬停弹窗
  • 复制节点到目标区域后,移除该节点的tooltip关联数据,避免弹窗继续生效

完整修改后的代码

library(jsTreeR)
library(shiny)

nodes <- list(
  list(
    text = "Menu",
    state = list(opened = TRUE),
    children = list(
      list(text = "Dog", type = "moveable", state = list(disabled = TRUE), data = list(tooltip = "一种常见的家养宠物,忠诚活泼")),
      list(text = "Cat", type = "moveable", state = list(disabled = TRUE), data = list(tooltip = "温顺独立的家养宠物,喜欢独处"))
    )
  ),
  list(text = ">>> Drag here <<<", type = "target", state = list(opened = TRUE))
)

dnd <- list(
  always_copy = TRUE,
  inside_pos = "last", 
  is_draggable = JS(
    "function(node) {",
    "  return node[0].type === 'moveable';",
    "}"
  )
)

mytree <- jstree(
  nodes, dragAndDrop = TRUE, dnd = dnd,
  types = list(moveable = list(), target = list())
)

script <- '
$(document).ready(function(){
  // 创建tooltip元素并添加到页面
  $("body").append("<div id=\"jstree-tooltip\" style=\"position:absolute; background:#333; color:white; padding:5px 10px; border-radius:4px; display:none; z-index:9999;\"></div>");
  var tooltip = $("#jstree-tooltip");

  // 监听Menu下moveable节点的鼠标进入事件
  $("#mytree").on("mouseenter", ".jstree-node[data-jstree-type=\"moveable\"]", function(e){
    // 仅Menu父节点下的触发tooltip
    if($(this).closest(".jstree-node").find(".jstree-anchor:contains(Menu)").length > 0){
      var tooltipText = $(this).data("jstree").data.tooltip;
      tooltip.text(tooltipText).show();
      // 定位到鼠标位置附近
      tooltip.css({
        top: e.pageY + 10 + "px",
        left: e.pageX + 10 + "px"
      });
    }
  });

  // 鼠标离开时隐藏tooltip
  $("#mytree").on("mouseleave", ".jstree-node[data-jstree-type=\"moveable\"]", function(){
    tooltip.hide();
  });

  // 鼠标移动时更新tooltip位置
  $("#mytree").on("mousemove", ".jstree-node[data-jstree-type=\"moveable\"]", function(e){
    if(tooltip.is(":visible")){
      tooltip.css({
        top: e.pageY + 10 + "px",
        left: e.pageX + 10 + "px"
      });
    }
  });

  var LETTERS = ["A", "B", "C"];
  var Visited = {};
  $("#mytree").on("copy_node.jstree", function(e, data){
    var oldid = data.original.id;
    var visited = Object.keys(Visited);
    if(visited.indexOf(oldid) === -1){
      Visited[oldid] = 0;
    }else{
      Visited[oldid]++;
    }
    var letter = LETTERS[Visited[oldid]];
    var node = data.node;
    var id = node.id;
    var index = $("#"+id).index() + 1;
    var text = index + ". " + node.text + " " + letter;
    Shiny.setInputValue("choice", text);
    var instance = data.new_instance;
    instance.rename_node(node, text);
    
    // 移除复制后节点的tooltip相关属性
    $("#" + id).removeData("jstree").removeAttr("data-jstree");
  });
});
'

ui <- fluidPage(
  tags$head(tags$script(HTML(script))),
  fluidRow(jstreeOutput("mytree"))
)

server <- function(input, output, session){
  output[["mytree"]] <- renderJstree(mytree)
}

shinyApp(ui, server)

关键细节说明

  • tooltip样式:设置深色背景、白色文字和高层级z-index,确保弹窗清晰且不被遮挡
  • 节点范围控制:通过父节点包含"Menu"文本的判断,仅原始Menu区域的节点触发弹窗
  • 复制后清理:在节点复制完成后,移除目标节点的data-jstree属性,彻底清除tooltip关联数据

内容的提问来源于stack exchange,提问作者Village.Idyot

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 13:55:17