搜索

首页  >  问答  >  正文

javascript - Jquery 自动导航高亮的相关问题,求大神指教.

想用JS直接实现系统菜单的导航高亮功能,其实就是自动检测页面url找到对应的元素addClass;不过因为本人菜鸟,实在写不出,求大神指教。

HTML结构如下(注释中就是基本需求)

<ul class="sidebar-menu" id="sidebar-menu">
    <!-- 这个无二级的直接给他的<li> addClass("active") 就可以 -->
    <li class=""><a href="{:url('index/index')}"><span>首页(1级无2级)</span></a></li>
    <li class="treeview">
        <a href="#"><span>系统管理(1级含2级)</span></a>
        <ul class="treeview-menu">
            <!-- 这种二级菜单除了给自己的li addClass("active")之外,
            还要给他的父元素也就是class=treeview_menu 加class=menu_open,
            还没完,treeview_menu的父元素treeview 也要addclass=active。-->
            <li><a href="admin/config/index"><i class="fa fa-circle-o"></i> 配置管理</a></li>
            <li><a href="admin/user/index"><i class="fa fa-circle-o"></i> 用户管理</a></li>
        </ul>
    </li>
</ul>

如上代码中的注释,我写了一个,已经基本满足上面的要求,但是还有一个需求,就是某些页面的url不在菜单里,也要让某个菜单高亮。比如上面的用户管理=admin/user/index ,进入用户管理肯定没问题,但是我还有一个 添加用户admin/user/add,进入添加用户这个页面的时候也想让 用户管理 href=admin/user/index这个菜单高亮,就不知道怎么写了。

我的想法是,访问页面的时候如果url存在在菜单中就自动高亮,如果不存在,如刚才说的添加用户admin/user/add,定义一个函数 如 set_nav_active(url) ,然后在admin/user/add这个页面 传入他想要高亮的菜单url ,比如se_nav_active(admin/user/index),但是,想法是有了,不知道怎么写了,求大神指教,或者大神有没有更好的办法

阿神阿神2773 天前207

全部回复(3)我来回复

  • 伊谢尔伦

    伊谢尔伦2017-04-11 09:09:58

    这个问题很有意思,总结一下,你要解决的问题有两个

    1. 根据当前 URL 找到匹配的 href 属性

    2. 找到对应第 1 找到的 href 属性的那个 <a>,对它以及它的几级父元素添加状态

    下面来一个一个的解决

    示例都是用的 es6 语法,不懂的话可以自己去 babel-try it out,或者 typescript playgournd 转换成 es5 的。

    找到匹配的 href

    首先,得让 href 和当前地址处于同样的目录级别表示。比如 href 里是 admin/....,那当前页面地址 window.location.pathname 也要处理成这样,比如当前地址可能是 /admin/... ,要去掉第一个 /;或者当前地址是 /appname/admin/....,要去掉前面的 /appname/。的过来,处理每个 href 去适配当前地址的表示也是一样的(其实这样更合理,只是处理起来更麻烦)

    const root = "/appname/";
    
    const menuUrls = $(".sidebar-menu a").map(function() {
        return `${root}${$(this).attr("href")}`;
    });

    这会得到包含所有链接的 href 属性的一个数组(jQuery 对象),我们可以遍历这个数组来寻找匹配的 URL,不过找到 URL 之后再去找对应的 <a> 是件麻烦事,所以,其实应该生成一个 map(JS 对象)

    const urlsMap = $(".sidebar-menu a")
        .toArray()
        .reduce((map, a) => {
            const $a = $(a);
            const url = `${root}${$a.attr("href")}`;
            map[url] = $a;
            return map;
        }, {});

    现在会得到一个 urlsMap,它是键是 URL,就是根据 href 处理而来的 URL。值是一个被 jQuery 封装的 <a> 的 DOM 对象,一一对应的关系。

    第一轮,精确匹配

    let pathname = window.location.pathname;
    let foundUrl = Object.keys(urlsMap)
        .fitler(url => {
            return url === pathname;
        })[0];

    如果 foundUrl 是一个字符串,说明找到了,不然就是没找到(因为 filter 的结果是个空数组,所以它的第 0 个元素是 undefined)。如果没找到,再进行第二轮,这时候需要截掉 pathname 的最后一部分,这也是 pathnamefoundUrllet 申明而不用 const 申明的原因

    第二轮,startsWith 匹配

    if (!foundUrl) {
        // 去掉最后一个 / 及其后的部分
        pathname = pathname.replace(/\/[^\/]+$/, "");
        foundUrl = Object.keys(urlsMap)
            .filter(url => {
                return url.startsWith(pathnem);
            })[0];
    }

    如果这样还没找到,你还需要再往前找,你可以考虑用一个循环代替上面的 if 分支,注意循环的结束条件,不要搞成死循环了。最终也没找到,那就拉倒吧。

    如果找到了,那么 foundUrl 对应的那个 <a> 就是要高亮的。这个 <a> 很好找,因为我们建了映射表

    const $a = urlsMap[foundUrl];

    下面进行第二步

    改变相关元素的状态

    这时候就需要找三个东西,<a> 外层的 <li>,再外层的 .treeview-menu 和再外层的 .treeview。如果都是直接父级关系,那很好找,依次取 .parent() 就好。如果不是,那就用 .closest(),下面的代码假设是这种情况

    $li = $a.closest("li");
    $treeViewMenu = $li.closest(".treeview-menu");
    $treeView = $treeViewMenu.closest(".treeview");

    都找到了,改个类就容易了(因为 jQuery 的容错性,完全不需要去判断找没找到,直接改就是了)

    $li.addClass("active");
    $treeViewMenu.addClass("menu_open");
    $treeView.addClass("active");

    搞定是搞定了,但是上面的代码都是直接写的,没测试,所以自己小心着用。

    回复
    0
  • ringa_lee

    ringa_lee2017-04-11 09:09:58

    document.querySelectorAll('xxxx')[{%$index%}].className ='active'

    回复
    0
  • 阿神

    阿神2017-04-11 09:09:58

    下面是我边学边写的,欢迎大神帮忙优化。

    • 首先所有页面调用这个函数 treeMenuActive() 不传参;

    • 访问个别页面且这个页面的URL地址不在菜单里时再次调用 treeMenuActive('admin/user/index') 并传参(想要高亮的菜单URL)

    PS:暂时测试没发现什么问题,不过 treeMenuActive() 在某些页面调用两次,先一次不传参,再一次传参,不知道这种情况会不会产生不可预料的问题,写成两个方法又好多代码重复,所以大神们有什么好想法呢。

    /* ------------
     * - 导航自动高亮
     * ------------
     * @author Talent 2016-11-21
     */
    function treeMenuActive(url){
        url = url || window.location.pathname;
        var $current = $("#sidebar-menu").find("a[href='" + url + "']");
        if ($current.length > 0){
            $current.parent().addClass("active");
            if ($current.closest('ul').attr('class') == 'treeview-menu')
            {
                $current.closest('ul').addClass("menu-open");
                $current.closest('ul').closest('li').addClass("active")
            }
        }
    }

    回复
    0
  • 取消回复