How to set menu item to active in sveltekit app - javascript

I have been working on a sveltekit application and recently ran into an issue with my main navigation menu. when the application is loaded or refreshed the current menu item that corresponds with the current URL is not set to active. (i.e. menu item 'Home' would have URL of '/') I had a hard time finding anything useful online demonstrating how to set this up, but I have a working solution and thought I'd document it for others. It is very simple at the moment.
I have a nav.json file where I define any menus within my application, making it easy to organize menus. I import that at the top of the file and store it in an array for use later.
import menuObj from '../nav/nav.json';
let sidebar = menuObj.menus[0].items;
onMount() is just getting the current path when the nav component is loaded, and only runs once for me.
then - using <svelte:window on:click={handlePageClick}/> we can listen to any clicks on the page.
In the handlePageClick(e) method we pass the on:click event. Using that event we can capture the current pathname of the URL. this allows us to check the pathname against our item.path which is defined in the nav.json file I've imported and extracted using the #each method in our html. On the <a> we can set the class to active if the items route matches the current path.
here is the full implementation
<script>
import menuObj from '../nav/nav.json';
import {onMount} from "svelte";
let path;
let sidebar = menuObj.menus[0].items;
onMount(() => {
path = window.location.pathname;
})
function handlePageClick(e) {
path = e.view.location.pathname;
}
</script>
<svelte:window on:click={handlePageClick}/>
<nav class="sidebar">
{#each sidebar as item, i}
<a href={item.route} id="sidebar-menu-item-{item.id}"
class={path === item.route ? "item active" : "item"}
>
<i id="sidebar-menu-item-{item.id}-i" class="{item.icon}"></i>
</a>
{/each}
</nav>
I haven't found any drawbacks to this yet. I will update this question if I find anything.

It's not really a question, but I will add an answer anyway because your method is not necessary, SvelteKit already has a built in system for this:
<script>
import { page } from '$app/stores';
</script>
<a href={item.route} class="item" class:active={$page.url.pathname == item.route}>
...
</a>
page is a store like object that among other things hold the current path, it will automatically be updated with the new path
as a bonus:
class:active is an easy shorthand, removing that ternary expression there.

Related

Next/Prev buttons to step through div contents

First of all a disclaimer, I'm not a dev. I'm halfway through The Odin Project and have covered some HTML and CSS, but, have not yet started on JS. In order to help with my learning I've created my own blog. My aim is for each blog post to have its own stylesheet (so with each new post I learn a little more about CSS).
Anyway, I plan to write a post about the benefits of using an eReader, specifically the Kindle. I've styled the page to look like a Kindle Oasis, and I'd like the reader to be able to step through the article contents via the Kindle's next/prev buttons, but, as I'm not a dev, this is where I'm stuck. Via Stack overflow I've managed to add some JS that will display page 1, 2 and 3 via dedicated buttons for each dive element, but, what I really need is to step through x number of pages via the prev/next buttons.
Here's what I have so far: https://codepen.io/dbssticky/pen/yLVoORO. Any help would be much appreciated. What I should do of course is finish The Odin Project and come up with a solution on my own, but, I'd really like to get this Kindle article published sooner rather than later. Hence my rather cheeky request for assistance.
Here's the JS I'm currently using:
function swapContent(id) {
const main = document.getElementById("main_place");
const div = document.getElementById(id);
const clone = div.cloneNode(true);
while (main.firstChild) main.firstChild.remove();
main.appendChild(clone);
}
You have the right idea and it just needs a few adjustments to get the previous/next functionality.
Currently your div IDs are following the format operation1, operation2, and so on. Since you want the previous/next functionality you'll need to change your 'swapping' function, which currently takes the full ID, to use the numeric portion only.
Add a new function which appends the number to 'operation' instead of using the whole thing:
function goToPage(pageNumber){
const main = document.getElementById("main_place");
const div = document.getElementById("operation" + pageNumber);
const clone = div.cloneNode(true);
while (main.firstChild) main.firstChild.remove();
main.appendChild(clone);
}
And then change your Page 1/2/3 buttons to use goToPage(1), goToPage(2) and so on.
Now for the previous/next functionality you'll need a way to track which page you're on, so that you can figure out which page to load.
Add a variable at the top (outside functions)
var currentPage = 0;
Then add a line in your goToPage function to track the page you're on.
currentPage = pageNumber;
Now that you're tracking you can add a previous and next function.
function goNextPage(){
goToPage(currentPage-1);
}
function goPreviousPage(){
goToPage(currentPage+1);
}
Then call it from the previous and next buttons.
<button onClick="goNextPage()" class="next-button"></button>
<button onClick="goPreviousPage()" class="previous-button"></button>
Here's a codepen: https://codepen.io/srirachapen/pen/WNZOXQZ
It's barebones and you may have to handle things like non existent div IDs.
HTML
<button class="next-button" onclick="nextContent()"></button>
<button class="previous-button" onclick="prevContent()"></button>
JS
var pageid = 0;
var maxpage = 3;
function nextContent() {
if(pageid == maxpage) return
pageid++
swapContent(`operation${pageid}`)
}
function prevContent() {
if(pageid == 1) return
pageid--
swapContent(`operation${pageid}`)
}
you can try this to switch between pages. But you may need to edit the "swapContent" method more sensibly.
Track the Current Page
Whatever solution you use to render pages & links (manual hardcoded links & content vs externally-stored & auto-generated), one thing is unavoidable: You need to track the current page!
var currentPage = 0
Then, any time there's a page change event, you update that variable.
With the current page being tracked, you can now perform operations relative to it (e.g. +1 or -1)
I'd suggest making a goToPage(page) function that does high-level paging logic, and keep your swapContent() function specifically for the literal act of swapping div content. In the future, you may find you'd want to use swapContent() for non-page content, like showing a "Welcome" or "Help" screen.
Example:
function goToPage(page) {
// Update `currentPage`
currentPage = page
// ... other logic, like a tracking event or anything else you want you occur when pages change
// Do the actual content swap, which could be your existing swapContent()
swapContent('operation'+page)
}
You'd invoke the function like so:
goToPage(3) // Jump to a specific page
goToPage(currentPage + 1) // Go to the next page
goToPage(currentPage - 1) // Go to the prev page
You can make separate helper functions like "goToNextPage()" if you desire, but for sure you start with a fundamental page-change function first.

Setting up a working URL with React Router

I'm having some real trouble setting up a react router and would really appreciate some suggestions. I have an app with multiple pages, and i want the app to display the page i'm on in url and be able to go to specific page through url. But i just cant seem to get it working.
The route path is:
<Route path="/page/{:id}" component={Body} />
It's calling Body, which is my main Class component where most of the code and logic is. But most of the threads and tutorials i've seen use either useEffect or useParams, which can't be done in a class component.
The app is simple, all it does is change between pages and calls api on every page to get different text and button numbers.
So what i'm doing now is i have a Link tag wrapped around my buttons with page numbers <Link key={index} to={`/page/${links.label}`}>. Those buttons are in a seperate function component. And i see the change in url based on which page i'm on. But i want to set it up so i can type the url directly.
buttons:
const renderPageNumbers = apiPagingSliced.map((links, index ) => {
return <Link key={index} to={`/page/${links.label}`}>
<button key={index} id={links.label}
onClick={props.handleClick}
>{links.label}
</button>
</Link>
})
and it just seems very incomplete. I tried using useParams, but couldn't get it to work.
Add this into your Body Component so when id param change your page get update and update your localStorage too
componentDidUpdate(prevProps, prevState, snapshot){
if(prevProps){
if(prevProps.match.params.id !== this.props.match.params.id){
const id = this.props.match.params.id;
localStorage.setItem("currentPage", id)
this.setState ({
currentPage: id});
this.fetchApi(id);
}
}
}

How can I change the background color of a current page's navigation button/link using JavaScript?

I want to write a JavaScript script (not JQuery, if at all possible) that changes the background color of a navigation button when its page is active. In other words, when I click on the "About" link and load the page, I want the "About" button to have a different background color so the user can see which page s/he is on.
Here is my HTML for the navigation links:
<nav>
<ul class="navbar">
<li class="navbutton">About</li>
<li class="navbutton">Local services</li>
<li class="navbutton">Intercity Services</li>
<li class="navbutton">Transport Circle Overview</li>
<li class="navbutton">Feedback</li>
</ul>
</nav>
Here is the CSS rule I want to apply when the page is active:
.navbutton-active {
background-color: rgb(24, 99, 6);
So far I've come up with this script although it isn't doing much except throw up an error:
document.getElementsByClassName('navbutton').addEventListener("DOMContentLoaded", btnActive());
function btnActive() {
document.getElementsByClassName('navbutton').classList.add('navbutton-active');
};
This is the error I get in the console:
TypeError: document.getElementsByClassName(...).classList is undefined
The behavior I'm aiming for is the following:
The page loads and the corresponding navigation button background changes to the color I've specified in the .navbutton-active CSS declaration.
When the user clicks on another navigation button, the color of the active page's navigation button changes to this color and the other navigation button colors remain their default.
This is probably simple but I can't figure it out.
I think the first step would be to find out which link is the one with an href property that matches the current URL.
// Obtain a collection of all links
var buttons = document.querySelectorAll('.navbutton');
var activeButton, button, link, i;
// Iterate through each link to find if its href matches the URL
for (i = 0; (button = buttons[i]); i++) {
link = button.firstElementChild;
// a console.log(link.href, window.location.href)
// might be useful here to ensure the comparisons will work
if (link.href === window.location.href) {
activeButton = button;
break;
}
}
// If a matching link was found, add the active class
if (activeButton) {
activeButton.classList.add('navbutton-active');
}
This URL comparison step isn't foolproof, and might need improvement. Really this should be done server side if you can.
Also the error in your original example is caused because getElementsByClassName() returns an array-like collection, which you would need to iterate through before interfacing with the classList property on the individual elements (similar to querySelectorAll() in my solution, but this is the newer API).
function btnActive()
{document.getElementById("navbutton").className = "navbutton-active ";}
also check if btnActive() is actually called by using alert("testing control"); in above function

Link to dynamic active class?

router for routing purpose in my react app.
Mine routing is defined in given manner:
<Route path={food/:cuisine/:pageNo} component={foodList}
and my navBar have following navigation:
<Link to={"/food/italian/1"} activeClassName="active">Italian</Link>
<Link to={"/food/indian/1"} activeClassName="active">Indian</Link>
but when visit page no. 2("/food/indian/2") of that component then myActive class of navBar is not showing.
If there are any alternate method to solve this problem then it will be great.
Thanks.
PS: I don't want to put all conditions like because the app has more than 15 different cuisine
if(this.props.params.cuisine=="Indian){
//then add .active in my Link to class
}
=======
just use conditional javascript for given case.
like :
if(this.props.params.cuisine=="Indian){
//then add .active in my Link to class
}
it will work perfectly for you. activeClass will compare whole route path and if it's true then they will add class active.
Alternate option: If u can create a constant value using splice function of javascript to get ur suffix of url .
then create two variable.
for example:
var indianPrefix= "/food/indian/"+ urlSuffix;
var italianPrefix= "/food/italian/"+ urlSuffix;
then use:
<Link to={italianPrefix} activeClassName="active">Italian</Link>
<Link to={indianPrefix} activeClassName="active">Indian</Link>
bug: By using this method on clicking the tab you will be landing on the same page always.
Thanks.

Fill a DOM element depending on my current route with Angularjs

I'm doing a multipage website and using some features of AngularJS, but I'm pretty new to it so would like some help. It uses ngview to load subpages, depending on the link clicked on my index file.
But on this index file I have a header that I would like to change depending on the current view. Sample:
<h1>
{{currentheader}}
</h1>
<ul class="nav navbar-nav">
<li ng-class="{ active: isActive('/')}">
Home
</li>
<li ng-class="{ active: isActive('/rooms')}">
Rooms
</li>
</ul>
<ng-view></ng-view>
I use a 'isActive' class on the navigation to set the active list item depending on my location.path(). Rendering the ngviews is working fine.
But i can't find a way to modify that header based on my active link. I could do that with jquery but I believe Angular has an easier sollution?
Thanks
EDIT:
found a sollution based on this
Update parent scope variable
An option that lets you put all the logic to update this header in one place, instead of dividing the logic into the controllers of your child views, is to listen for a $locationChangeSuccess within the controller of your parent view.
function updateHeader($scope,val) {
$scope.currentheader = val; // Obviously you'd make this more intelligent.
}
$scope.currentheader = "Initial Value";
$scope.$on('$locationChangeSuccess', function (event, toUrl, fromUrl) {
updateHeader($scope,toUrl);
});

Categories

Resources