Skip to content

Create a dynamic category list

Moved Let's Build It
  • I’ll revise the original post to reflect this

  • I’ve been playing around with this today, and created a “tree” view

    image.png

  • If you’d like the code for this, then it’s below (it’s essentially a “fork” of the original to allow for extra classes)

    JS

    $(document).ready(function() {
        $.getJSON('/api/categories', function(data, status) {
            $.each(data.categories, function(key, value) {
                var categorylist = $(" \
    	<li class='dropdown-item tree-root'><span class='category-menu'><i class='fal " + this.icon + "'></i><a style='display: inherit;' class='dropdown-item rounded-1' href='/category/" + this.slug + "'>" + this.name + "</a></span></li> \
    		<ul class='tree-branch' style='list-style: none;'>" +
                    this.children.map(c => `<li class='dropdown-item tree-node'><span class='category-menu-tree-node'><i class='fal ${c.icon}'></i><a class='dropdown-item rounded-1' style='display: inherit;' href='/category/${c.slug}'>${c.name}</a></span></li>`).join(" ") +
                    "</ul>"
                );
                if ($(window).width() < 767) {
                    $(".bottombar #thecategories").append(categorylist);
                } else {
                    $(".sidebar-left #thecategories").append(categorylist);
                }
            });
        });
    });
    

    CSS

    ul.tree-branch {
        border-left: 1px solid var(--bs-border-color);
        margin-left: 22px;
    }
    li.tree-node:before {
        border-bottom: 1px solid var(--bs-border-color);
        position: relative;
        top: -0.3em;
        height: 1em;
        width: 30px;
        content: "";
        display: inline-block;
        left: -48px;
    }
    span.category-menu-tree-node {
        margin-left: -35px;
    }
    

    NOTE: I use variables for all of my CSS, so you’ll need to substitute var with your own HEX values. Also note, that you still need the original CSS from the first post for this to work - what is listed above only provides you with the tree, branches, and nodes effect.

  • @phenomlab is there something wrong with the category list panel?

    edit: it is fixed now. When I minimize the screen size on my computer and then expand it again, the panel goes blank. But it is fixed when I refresh the page, I guess this is something expected.

  • @crazycells No, not at all. I can’t reproduce this, but that does sound like a hardware acceleration issue. What happens if you disable that (I’m assuming Chrome here if of course, but the same applies for other browsers)

    https://pureinfotech.com/disable-hardware-acceleration-chrome/

  • @phenomlab sorry, my mistake, it is fixed now.
    I am using Firefox, but this morning I needed to restart to update Firefox, so I believe I was having the problem yesterday night because of the outdated browser.

  • @crazycells Good. Glad to hear it’s an even simpler fix 🙂

  • phenomlabundefined phenomlab moved this topic from Customisation on
  • Just circling back here as I’ve been helping @cagatay this morning on his site, and noticed that if you use a mixture of fa-brands and fa-solid then the code supplied above may produce some odd looking results.

    If this is the case, replace the function with this

    $(document).ready(function() {
        $.getJSON('/api/categories', function(data, status) {
            $.each(data.categories, function(key, value) {
                var iconClass = 'fa'; // Default to 'fa' if the icon type is not recognized
                
                // Check if the icon is FontAwesome Unicode
                if (this.icon.startsWith('&#x') || this.icon.startsWith('&#xf')) {
                    iconClass = 'fa';
                } else if (this.icon.startsWith('fab')) {
                    iconClass = 'fab';
                }
                
                var categorylist = $(" \
        <li class='dropdown-item tree-root'><span class='category-menu'><i class='" + iconClass + " " + this.icon + "'></i><a style='display: inherit;' class='dropdown-item rounded-1' href='/category/" + this.slug + "'>" + this.name + "</a></span></li> \
            <ul class='tree-branch' style='list-style: none;'>" +
                    this.children.map(c => {
                        var childIconClass = 'fa'; // Default to 'fa' for child icons
                        
                        // Check if the child icon is FontAwesome Unicode
                        if (c.icon.startsWith('&#x') || c.icon.startsWith('&#xf')) {
                            childIconClass = 'fas';
                        } else if (c.icon.startsWith('fab')) {
                            childIconClass = 'fab';
                        }
                        
                        return `<li class='dropdown-item tree-node'><span class='category-menu-tree-node'><i class='${childIconClass} ${c.icon}'></i><a class='dropdown-item rounded-1' style='display: inherit;' href='/category/${c.slug}'>${c.name}</a></span></li>`;
                    }).join(" ") +
                    "</ul>"
                );
                
                if ($(window).width() < 767) {
                    $(".bottombar #thecategories").append(categorylist);
                } else {
                    $(".sidebar-left #thecategories").append(categorylist);
                }
            });
        });
    });
    
    

    In fact, if you want to replace it anyway to make your experience “future proof”, you can use this code now 🙂

  • I never really liked the way this was generated, as it didn’t exactly match the other dropdowns. I’ve since rectified that here, and will now update GIT.

    Revised navigation code

    <li class='nav-item' title=''>                 
        <a class='nav-link nav-btn navigation-link px-3 py-2' href='/categories'>                     
            <span class='d-inline-flex justify-content-between align-items-center w-100'>                         
                <span class='text-nowrap'>                             
                    <i class='fa-regular fa-list fa-solid menu-icon' data-content='' aria-hidden='true'>               </i>                             
                    <span class='nav-text px-2 fw-semibold'>All Categories</span>                         
                </span>                         
                <span component='navigation/count' class='badge rounded-1 bg-primary hidden'></span>                     
            </span>                 
        </a>                 
        <ul class='tree-branch' style='list-style: none;'></ul>            
     </li>
     <li id='thecategories'><h6 class='dropdown-header text-xs'>Individual Categories</h6></li>
    

    Revised category list code

    
    $(document).ready(function() {
        $.getJSON('/api/categories', function(data, status) {
            $.each(data.categories, function(key, value) {
                var iconClass = 'fa-regular'; // Default to 'fa-regular' if the icon type is not recognized
                
                // Check if the icon is FontAwesome Unicode
                if (this.icon.startsWith('&#x') || this.icon.startsWith('&#xf')) {
                    iconClass = 'fa-regular';
                } else if (this.icon.startsWith('fab')) {
                    iconClass = 'fab';
                }
                
                var categorylist = $("<li class='nav-item' title=''> \
                    <a class='nav-link nav-btn navigation-link px-3 py-2' href='/category/" + this.slug + "'> \
                        <span class='d-inline-flex justify-content-between align-items-center w-100'> \
                            <span class='text-nowrap'> \
                                <i class='" + iconClass + " " + this.icon + " menu-icon' data-content='' aria-hidden='true'></i> \
                                <span class='nav-text px-2 fw-semibold'>" + this.name + "</span> \
                            </span> \
                            <span component='navigation/count' class='badge rounded-1 bg-primary hidden'></span> \
                        </span> \
                    </a> \
                    <ul class='tree-branch' style='list-style: none;'>" + 
                    this.children.map(c => {
                        var childIconClass = 'fa-regular'; // Default to 'fa-regular' for child icons
                        
                        // Check if the child icon is FontAwesome Unicode
                        if (c.icon.startsWith('&#x') || c.icon.startsWith('&#xf')) {
                            childIconClass = 'fa-regular';
                        } else if (c.icon.startsWith('fab')) {
                            childIconClass = 'fab';
                        }
                        
                        return `<li class='nav-item tree-node' title=''><a class='nav-link nav-btn navigation-link px-3 py-2' href='/category/${c.slug}'><span class='d-inline-flex justify-content-between align-items-center w-100'><span class='text-nowrap'><i class='${childIconClass} ${c.icon} menu-icon' data-content='' aria-hidden='true'></i><span class='nav-text px-2 fw-semibold'>${c.name}</span></span><span component='navigation/count' class='badge rounded-1 bg-primary hidden'></span></span></a></li>`;
                    }).join(" ") + 
                    "</ul> \
                </li>");
                
                if ($(window).width() < 767) {
                    $(".bottombar #thecategories").append(categorylist);
                } else {
                    $(".sidebar-left #thecategories").append(categorylist);
                }
            });
        });
    });
    

    Minor CSS changes (only the styles listed below)

    ul.tree-branch {
        border-left: 1px solid var(--bs-link-color);
        margin-left: 22px;
    }
    
    li.tree-node:before {
        border-bottom: 1px solid var(--bs-link-color);
        position: relative;
        top: 1.4em;
        width: 35px;
        content: "";
        display: flex;
        left: -33px;
    }
    
  • Hmm - seems I never committed this code. I’ll do that now…

    EDIT - here it is

    https://github.com/phenomlab/category-list/tree/main


Related Topics
  • Protecting API Access on Apache/Cloudways

    Solved Security
    3
    1 Votes
    3 Posts
    106 Views

    @phenomlab issue was with high traffic spikes and the website used to get crashed. API is managed by others, its built in such a way they built it in such un protected way. we would be moving to nodejs own APIs soon to address all these issues. thought of solving it with help of you and a friend of mine is helping me build a new site with APIs. thanks

  • 5 Votes
    3 Posts
    1k Views

    Very good like always 😉

  • What's your go to product for site stats?

    Let's Build It
    27
    15 Votes
    27 Posts
    2k Views

    For anyone else coming here and is struggling to get pm2 to work with Umami (as I did - it started, but never seemed to work after a reboot which is pretty useless), you can use the below. Obviously, change the parts noted inside the [brackets]. Follow the below instructions:

    Instructions

    Open a terminal and create a new systemd service file:

    sudo nano /etc/systemd/system/umami.service

    Add the following content to the file:

    [Unit] Description=Umami Analytics Server After=network.target [Service] Type=simple User=[umami user] WorkingDirectory=[path to umami] ExecStart=/usr/local/bin/node [path to umami]/node_modules/.bin/next start Restart=on-failure [Install] WantedBy=multi-user.target

    Replace [umami user] with the username of the user that should run the Umami service, and [path to umami] with the actual path to your Umami installation.

    Save the file and exit the editor.

    Reload the systemd manager configuration:

    sudo systemctl daemon-reload

    Enable the Umami service to start on boot:

    sudo systemctl enable umami.service

    Start the Umami service:

    sudo systemctl start umami.service

    You can check the status of the service with:

    sudo systemctl status umami.service

    This systemd service file will ensure that Umami starts automatically when the system boots, and it will restart the service if it fails. Remember to adjust the WorkingDirectory and ExecStart paths according to where Umami is installed on your system, and ensure that Node.js is installed and accessible at /usr/bin/node (or adjust the path to Node.js as necessary).

  • Threaded post support for NodeBB

    Let's Build It
    146
    50 Votes
    146 Posts
    21k Views

    Updated git for above change

    https://github.com/phenomlab/nodebb-harmony-threading/commit/14a4e277521d83d219065ffb14154fd5f5cfac69

  • Following the API docs but its not clear ...

    Solved Customisation
    8
    2 Votes
    8 Posts
    509 Views

    @Panda you’d be surprised. If you consider that you’d need to use the API to be able to populate a WordPress widget for example (which in turn would of course be PHP), taking this route is still immensely popular.

  • Setup OGProxy for use in NodeBB

    Moved Let's Build It
    110
    21 Votes
    110 Posts
    11k Views

    @crazycells said in Setup OGProxy for use in NodeBB:

    are they cached for each user separately?

    No. It’s a shared cache

    @crazycells said in Setup OGProxy for use in NodeBB:

    additionally, this is also handling youtube videos etc, right?

    No. This is handled by nodebb-plugin-ns-embed

  • About this category

    Pinned Let's Build It
    5
    2 Votes
    5 Posts
    525 Views

    I’m going to be adding some new posts to the labs category, and will use this going forward when writing code that could easily be adopted by others (a great example is the OGProxy, which I will move here).

    If you have any ideas of would like a walkthrough of how to set something up, then this is the place it should go.

  • 4 Votes
    10 Posts
    886 Views

    @phenomlab yeah its a beautiful world, i’m trying to have some practice

    https://rapidapi.com/user/justoverclockl

    i’ve released two simple api for now, but i’m try to learn as much as i can 🙂