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

如何让Treeview完全支持RTL并将外层UL设为Bootstrap Timeline

解决方案

问题根源

  1. 同时引入不同版本Bootstrap(v4和v5 RTL),样式冲突导致RTL失效
  2. 自定义Treeview样式硬编码left/margin-left等LTR属性,未适配RTL
  3. 外层ul未添加timeline类

修改步骤

1. 修复HTML结构与依赖引入

  • 移除Bootstrap 4的CSS链接,仅保留Bootstrap 5 RTL版本
  • 给外层ul(#tree2)添加timeline类
  • 给页面容器添加dir="rtl"确保全局文本方向

2. 适配RTL的CSS调整

将所有LTR方向的属性替换为RTL对应属性:

  • margin-left → margin-right
  • border-left → border-right
  • left → right
  • 调整图标、时间线标记的位置

3. 合并优化JS代码

移除重复的treed方法实现,保留带状态标记的版本,避免功能冲突


完整代码

HTML

<div class="row" dir="rtl">
    <div class="col-md-4">
       <ul id="tree2" class="timeline">
          <li class="timeline-item">
             <a href="#">TECH</a>
             <ul>
                <li>Company Maintenance</li>
                <li class="timeline-item">
                   Employees
                   <ul>
                      <li class="timeline-item">
                         Reports
                         <ul>
                            <li class="timeline-item">Report1 
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#exampleModal" data-bs-whatever="@fat">
<i class="fa fa-plus"></i>
</button>
<div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
      </div>
      <div class="modal-body">
        <form>
          <div class="mb-3">
            <label for="recipient-name" class="col-form-label">نص العنصر</label>
            <input type="text" class="form-control">
          </div>
        </form>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">إغلاق</button>
        <button type="button" class="btn btn-primary">إرسال</button>
      </div>
    </div>
  </div>
</div> </li>                            
<li class="timeline-item">Report2 
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#exampleModal" data-bs-whatever="@fat">
<i class="fa fa-plus"></i>
</button>
</li>                              
<li class="timeline-item">Report3 
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#exampleModal" data-bs-whatever="@fat">
<i class="fa fa-plus"></i>
</button>
</li>
                         </ul>
                      </li>
                      <li class="timeline-item">Employee Maint.</li>
                   </ul>
                </li>
                <li class="timeline-item">Human Resources</li>
             </ul>
          </li>
          <li class="timeline-item">
             XRP
             <ul>
                <li>Company Maintenance</li>
                <li class="timeline-item">
                   Employees
                   <ul>
                      <li class="timeline-item">
                         Reports
                         <ul>
                            <li class="timeline-item">Report1 
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#exampleModal" data-bs-whatever="@fat">
<i class="fa fa-plus"></i>
</button>
</li>
                            <li class="timeline-item">Report2
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#exampleModal" data-bs-whatever="@fat">
<i class="fa fa-plus"></i>
</button>
</li>
                            <li class="timeline-item">Report3 
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#exampleModal" data-bs-whatever="@fat">
<i class="fa fa-plus"></i>
</button>
</li>
                         </ul>
                      </li>
                      <li class="timeline-item">Employee Maint.</li>
                   </ul>
                </li>
                <li class="timeline-item">Human Resources</li>
             </ul>
          </li>
       </ul>
    </div>
 </div>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/css/bootstrap.rtl.min.css">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.0/css/all.css" integrity="sha384-lZN37f5QGtY3VHgisS14W3ExzMWZxybE1SJSEsQp9S+oqd12jhcu+A56Ebc1zFSJ" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-HwwvtgBNo3bZJJLYd8oVXjrBZt8cqVSpeBNS5n7C8IVInixGAoxmnlMuBnhbgrkm" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.js"></script>

CSS

.tree, .tree ul {
  margin:0;
  padding:0;
  list-style:none;
  margin-right:10px;
}
.tree ul {
  margin-right:1em;
  position:relative
}
.tree ul ul {
  margin-right:.5em
}
.tree ul:before {
  content:"";
  display:block;
  width:0;
  position:absolute;
  top:0;
  bottom:0;
  right:0;
  border-right:1px solid
}
.tree li {
  margin:0;
  padding:0 1em;
  line-height:2em;
  color:#369;
  font-weight:700;
  position:relative;
}
.tree ul li:before {
  content:"";
  display:block;
  width:10px;
  height:0;
  border-top:1px solid;
  margin-top:-1px;
  position:absolute;
  top:1em;
  right:0
}
.tree ul li:last-child:before {
  background:#fff;
  height:auto;
  top:1em;
  bottom:0
}
.indicator {
  margin-left:5px;
}
.tree li a {
  text-decoration: none;
  color:#369;
}
.tree li button, .tree li button:active, .tree li button:focus {
  text-decoration: none;
  color:#369;
  border:none;
  background:transparent;
  margin:0;
  padding:0;
  outline: 0;
}

.loading {
  font-style: italic;
}
.timeline {
  border-right: 1px solid hsl(0, 0%, 90%);
  position: relative;
  list-style: none;
}

.timeline .timeline-item {
  position: relative;
}

.timeline .timeline-item:after {
  position: absolute;
  display: block;
  top: 0;
}

.timeline .timeline-item:after {
  background-color: hsl(0, 0%, 90%);
  right: -38px;
  border-radius: 50%;
  height: 11px;
  width: 11px;
  content: "";
}

JS

$.fn.extend({
  treed: function (o) {
    var openedClass = 'fa-minus-circle';
    var closedClass = 'fa-plus-circle';

    if (typeof o != 'undefined'){
      if (typeof o.openedClass != 'undefined'){
        openedClass = o.openedClass;
      }
      if (typeof o.closedClass != 'undefined'){
        closedClass = o.closedClass;
      }
    };

    //initialize each of the top levels
    var tree = $(this);
    tree.addClass("tree");
    tree.find('li').has("ul").each(function () {
        var branch = $(this); //li with children ul
        branch.prepend("<i class='indicator fas " + closedClass + "'></i>");
        branch.addClass('branch');

        // declare a flag to toggle
        let flag = false;
        branch.on('click', function (e) {
            if (this == e.target) {
                // toggle on click
                flag = !flag;
                var icon = $(this).children('i:first');
                icon.toggleClass(openedClass + " " + closedClass); 

                // show loader only when its opened, and collpase once toggled   
                if(flag) {
                  $(this).append("<div class='loading'><span class='fa fa-spinner fa-spin'></span></div>"); 
                  setTimeout(()=>{
                    $(this).find('.loading').remove();
                    $(this).children().children().slideToggle(400);
                  }, 400)
                } else {
                  $(this).children().children().slideToggle(400);
                }
            }
        })
        branch.children().children().toggle();
    });

    //fire event from the dynamically added icon
    tree.find('.branch .indicator').each(function(){
      $(this).on('click', function () {
          $(this).closest('li').click();
      });
    });

    //fire event to open branch if the li contains an anchor instead of text
    tree.find('.branch>a').each(function () {
        $(this).on('click', function (e) {
            $(this).closest('li').click();
            e.preventDefault();
        });
    });

    //fire event to open branch if the li contains a button instead of text
    tree.find('.branch>button').each(function () {
        $(this).on('click', function (e) {
            $(this).closest('li').click();
            e.preventDefault();
        });
    });
  }
});

//Initialization of treeviews
$('#tree2').treed({openedClass:'fa-folder-open', closedClass:'fa-folder'});

内容的提问来源于stack exchange,提问作者Andandydongola

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 14:30:57