How to highlight the selected bootstrap tab in React - javascript

I am using react for my application, and I have a bootstrap navigation bar menu has three tabs. I want to highlight the selected tab.
Here is the html code in my render() function.
<div className="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul className="nav navbar-nav navbar-left">
<li onClick={event => this.handleClick('tab1', event)} className={(this.state.activeTabClassName === "tab1") ? "active" : ""}>HOME<span className="sr-only">(current)</span></li>
<li onClick={event => this.handleClick('tab2', event)} className={(this.state.activeTabClassName === "tab2") ? "active" : ""}>RESOURCES</li>
<li onClick={event => this.handleClick('tab3', event)} className={(this.state.activeTabClassName === "tab3") ? "active" : ""}>CONTACT</li>
</ul>
</div>
I use the handleClick function to change the tabName:
handleClick(tabName, event) {
this.setState ({activeTabClassName : tabName});
//this.state.activeTabClassName = tabName;
}
I also set the initial state of activeTabClassName as 'tab1' in my constructor:
constructor() {
super();
this.state = {activeTabClassName : "tab1"};
// this.handleClick = this.handleClick.bind(this);
}
The code for the whole Nav.js component
import React from 'react';
export default class Nav extends React.Component {
constructor() {
super();
this.state = {activeTabClassName : "tab1"};
}
handleClick(tabName, event) {
this.setState ({activeTabClassName : tabName});
}
render(){
return(
<nav className="navbar navbar-default" role="navigation">
<div className="container-fluid">
<div className="navbar-header">
<button type="button" className="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span className="sr-only">Toggle navigation</span>
<span className="icon-bar"></span>
<span className="icon-bar"></span>
<span className="icon-bar"></span>
</button>
<a className="navbar-brand navbar-utimg" href="https://www.xxx.xxxx" target="_blank">
<img alt="logo" src="/dist/assets/coe-logo.png"/>
</a>
</div>
<div className="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul className="nav navbar-nav navbar-left">
<li onClick={event => this.handleClick('tab1', event)} className={(this.state.activeTabClassName === "tab1") ? "active" : ""}>HOME<span className="sr-only">(current)</span></li>
<li onClick={event => this.handleClick('tab2', event)} className={(this.state.activeTabClassName === "tab2") ? "active" : ""}>RESOURCES</li>
<li onClick={event => this.handleClick('tab3', event)} className={(this.state.activeTabClassName === "tab3") ? "active" : ""}>CONTACT</li>
</ul>
<form className="navbar-form navbar-right" role="search">
<div className="form-group">
<input type="text" className="form-control" placeholder="Search"/>
</div>
<button type="submit" className="btn btn-default">Submit</button>
</form>
</div>
</div>
</nav>
)
}
}
However, it does not work well. Everytime when click the tab, it does not highlight the selected tab unless I clicked it again. Why does the onClick function not change the state right away? Is there any better solution for this problem?
Thanks!

Related

Function sent as props being called continuously - React.js

I am making a newsapp i want it to have different categories, instead of using react-router-dom for navigation what i want to do is create a state object and create a key in it named category and set current category as it's value and i have sent that key as a props to news component where i fetch news and embed that category to the fetch URL
I have made a function to set the category and its in app.js I have sent it to navbar component as props, the issue i am facing is that i can't select a category, because for some reason the onClick of the very last category is being called continuously and I know this because I console logged the category in setCategory function, can anyone tell me why this is happening
code in app.js:
import './App.css';
import React, { Component } from 'react'
import Navbar from './components/Navbar';
import News from './components/News';
export default class App extends Component {
constructor() {
super()
this.state = {
darkMode: "light",
country: "us",
category: "general",
key: "general"
}
}
setCategory = (cat)=> {
this.setState({
category: cat
})
console.log(this.state.category)
}
setCountry = (cntry)=> {
this.setState({
category: cntry
})
}
setDarkMode = () => {
if (this.state.darkMode === "light") {
this.setState({ darkMode: "dark" })
document.body.style.backgroundColor = "black"
} else {
this.setState({ darkMode: "light" })
document.body.style.backgroundColor = "white"
}
}
render() {
return (
<div>
<Navbar setCategory={this.setCategory} setCountry={this.setCountry} setDarkMode={this.setDarkMode} darkMode={this.state.darkMode} />
<News key={this.state.category} category={this.state.category} country={this.state.country} pageSize={18} darkMode={this.state.darkMode} />
</div>
)
}
}
code in Navbar component:
import React, { Component } from 'react'
export default class Navbar extends Component {
constructor(props) {
super(props)
this.setCategory = this.props.setCategory
}
render() {
return (
<div>
<nav className={`navbar navbar-expand-lg navbar-${this.props.darkMode} bg-${this.props.darkMode}`}>
<a className="navbar-brand" href="/">NewsMonkey</a>
<button className="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span className="navbar-toggler-icon"></span>
</button>
<div className="collapse navbar-collapse" id="navbarSupportedContent">
<ul className="navbar-nav mr-auto">
<li className="nav-item active">
<a className="nav-link" href="/">Home <span className="sr-only">(current)</span></a>
</li>
<li className="nav-item">
<a className="nav-link" href="/about">About</a>
</li>
<li className="nav-item dropdown">
<a className="nav-link dropdown-toggle" href="/" role="button" data-toggle="dropdown" aria-expanded="false">
Categories
</a>
<div className="dropdown-menu">
<p className="dropdown-item cursor-pointer" onClick={this.setCategory("business")}>Business</p>
<p className="dropdown-item cursor-pointer" onClick={this.setCategory("science")}>Science</p>
<p className="dropdown-item cursor-pointer" onClick={this.setCategory("technology")}>Technology</p>
<p className="dropdown-item cursor-pointer" onClick={this.setCategory("entertainment")}>Entertainment</p>
<p className="dropdown-item cursor-pointer" onClick={this.setCategory("health")}>Health</p>
<p className="dropdown-item cursor-pointer" onClick={this.setCategory("sports")}>Sports</p>
<div className="dropdown-divider"></div>
<p className="dropdown-item cursor-pointer" onClick={this.setCategory("general")}>General</p>
</div>
</li>
</ul>
{/* <form className="form-inline my-2 my-lg-0">
<input className="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search" />
<button className="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
</form> */}
<div className={`custom-control custom-switch text-${this.props.darkMode === "light" ? "dark" : "light"}`}>
<input type="checkbox" className="custom-control-input" onClick={this.props.setDarkMode} id="customSwitch1" />
<label className={`custom-control-label`} htmlFor="customSwitch1">Dark mode</label>
</div>
</div>
</nav>
</div>
)
}
}
This doesn't do what you think it does:
onClick={this.setCategory("business")}
This calls the setCategory function immediately and uses the result of that function as the onClick handler. Don't pass the result of calling a function to onClick, pass a function itself:
onClick={() => this.setCategory("business")}

How to run a function using onClick in React

When I press the sync button, I need to run the Check Routine function. How can I do this ?
const CheckRoutine = require('../routines/check-at');
export default ({ className }) => (
<ul className={ `nav flex-column ${className || ''}` }>
<li>
<button className="btn btn-primary"
onClick={CheckRoutine} >
<i className="fa fa-refresh"> </i>
<span>Sync</span>
</button>
</li>
</ul>
);```
check-at:
module.exports = atCheck;
function atCheck() {
console.log("Cheking...");
}
If your check Routine is in the same component, you can directly give,
<button className="btn btn-primary"
onClick={CheckRoutine} >
<i className="fa fa-refresh"> </i>
<span>Sync</span>
</button>
or If your Check routine is in parent component, you can use call back function inside your component like below.
const CheckRoutine =(event) =>{
event.preventDefault();
props.checkRoutine();
}
<button className="btn btn-primary"
onClick={CheckRoutine} >
<i className="fa fa-refresh"> </i>
<span>Sync</span>
</button>
you can use this,
CheckRoutine.js
const CheckRoutine = e => {
console.log(e)
}
export default CheckRoutine
fileName.js
import CheckRoutine from './CheckRoutine'
export default ({ className }) => (
<ul className={ `nav flex-column ${className || ''}` }>
<li>
<button className="btn btn-primary"
onClick={CheckRoutine} >
<i className="fa fa-refresh"> </i>
<span>Sync</span>
</button>
</li>
</ul>
);
or you can in single file
const CheckRoutine = e => {
console.log(e)
}
export default ({ className }) => (
<ul className={ `nav flex-column ${className || ''}` }>
<li>
<button className="btn btn-primary"
onClick={CheckRoutine} >
<i className="fa fa-refresh"> </i>
<span>Sync</span>
</button>
</li>
</ul>
);
import atCheck from '../routines/check-at'
export default ({ className }) => (
<ul className={ `nav flex-column ${className || ''}` }>
<li>
<button className="btn btn-primary"
onClick={atCheck} >
<i className="fa fa-refresh"> </i>
<span>Sync</span>
</button>
</li>
</ul>
);
//../routines/check-at
export default const atCheck => (e) {
console.log("Cheking...");
}

How to use JavaScript with $ inside render() in Reactjs

I work on content editor in React admin interface.
And I'd love to install my favorite block content editor. But it's an old one and have no react version.
I know how to link .js and .css in head with ReactHelmet
But have no idea how to run following script:
<script>
$(function () {
$("#editor").brickyeditor({
ignoreHtml: true,
blocksUrl: 'data.json',
templatesUrl: 'templates.html',
onChange: function(data) {
console.log(data.html);
}
});
});
</script>
Here is initial html structure
<body>
<header>
<nav class="container navbar navbar-expand-lg navbar-light">
<a class="navbar-brand" href="index.html">BrickyEditor</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
<li class="nav-item active">
<a class="nav-link" href="http://brickyeditor.info/examples.html">More Examples</a>
</li>
<li class="nav-item">
<a class="nav-link" href="https://github.com/yakovlevga/brickyeditor">GitHub Repository</a>
</li>
</ul>
</div>
</nav>
</header>
<main>
<div class="container">
<div class="row">
<div class="col-md-12">
<div id="editor"></div>
</div>
</div>
</div>
</div>
</main>
</body>
Im using it like so:
import PageTitle from "../components/common/PageTitle";
import Helmet from "react-helmet";
import $ from 'jquery';
class NewsEditor extends React.Component {
constructor(props) {
super(props);
this.state = {
};
}
render() {
const {
} = this.state;
return (
<Container fluid className="main-content-container px-4">
{/* Page Header */}
<Row noGutters className="page-header py-4">
<PageTitle sm="4" title="News editor" subtitle="Drag and drop interface" className="text-sm-left" />
</Row>
<Helmet
title="Nested Title"
link={[
{"rel": "stylesheet", "href": "https://cdn.jsdelivr.net/npm/brickyeditor/dist/jquery.brickyeditor.min.css"}
]}
script={[
{"src": "https://cdn.jsdelivr.net/npm/brickyeditor/dist/jquery.brickyeditor.min.js"},
]}
/>
<header>
<script>
$(function () {
$("#editor").brickyeditor({
ignoreHtml: true,
blocksUrl: 'data.json',
templatesUrl: 'templates.html',
onChange: function(data) {
console.log(data.html);
}
});
});
</script>
<nav class="container navbar navbar-expand-lg navbar-light">
<a class="navbar-brand" href="index.html">BrickyEditor</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
<li class="nav-item active">
<a class="nav-link" href="http://brickyeditor.info/examples.html">More Examples</a>
</li>
<li class="nav-item">
<a class="nav-link" href="https://github.com/yakovlevga/brickyeditor">GitHub Repository</a>
</li>
</ul>
</div>
</nav>
</header>
<main>
<div class="container">
<div class="row">
<div class="col-md-12">
<div id="editor"></div>
</div>
</div>
</div>
</main>
</Container>
);
}
}
export default NewsEditor;
On this stage all I have is Failed to compile error.
UPD: Following advices I keep on getting TypeErrors
I always make re-usable components for external libraries. So in your case, it would be BrickyEditor component which could look like this:
class BrickyEditor extends React.Component {
editorRef = React.createRef();
componentDidMount() {
window.$(this.editorRef.current).brickyeditor(this.props);
}
render() {
return <div ref={this.editorRef}></div>
}
}
// in your NewsEditor component you can use it like so
<BrickyEditor
ignoreHtml={true}
blocksUrl="data.json"
templatesUrl="templates.html"
onChange={function(data) {
console.log(data.html);
}}
/>

VueJS close Hamburger Menu on Route Change

I have a simple VueJS App with a Navigation Bar from Bootstrap:
<template>
<header id="header">
<nav class="navbar mynavbar navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="logo" href="index.html"><img src="images/logo.png" alt=""></a>
</div>
<div class="collapse navbar-collapse" id="navbar-collapse">
<ul class="nav navbar-nav navbar-right">
<li><router-link to="/home"><a>Home</a></router-link></li>
<li><router-link to="/about"><a>About Us</a></router-link></li>
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container -->
</nav>
</header>
</template>
Now I want to ensure that when I change the route, the Bootstrap Menu gets closed. What is the best way to accomplish this?
Instead of adding event handlers to every router-link, you can simply watch the $route property for changes:
<script>
export default {
watch: {
'$route' () {
$('#navbar-collapse').collapse('hide');
}
}
}
</script>
Without Bootstrap and jQuery, can be easily achieved using class toggle and watch route changes.
<a role="button" class="navbar-burger" aria-label="menu" aria-expanded="false"
#click="toggledNav = !toggledNav"
>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</a>
Script:
export default {
data () {
return {
toggledNav: false
}
},
watch: {
'$route' () {
this.toggledNav = false
}
}
}
You could give this a try:
<template>
<header id="header">
<nav class="navbar mynavbar navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="logo" href="index.html"><img src="images/logo.png" alt=""></a>
</div>
<div class="collapse navbar-collapse" id="navbar-collapse">
<ul class="nav navbar-nav navbar-right">
<li><router-link #click.native="closeMenu()" to="/home"><a>Home</a></router-link></li>
<li><router-link #click.native="closeMenu()" to="/about"><a>About Us</a></router-link></li>
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container -->
</nav>
</header>
</template>
<script>
export default {
methods: {
closeMenu() {
$('#navbar-collapse').collapse('hide');
}
}
}
</script>
With vue3 and bootstrap5, adding this to the router-link element:
data-bs-toggle="collapse" data-bs-target=".navbar-collapse"
did not work for me: the menu would close without the link being followed.
So, I added this to router/index.js:
router.beforeEach(() => {
document.getElementById('navbarCollapse').classList.remove('show');
})
with navbarCollapse being the id of the div holding the menu/nav items.
It seemed to do the trick.
If necessary you can adjust the hamburger button state: its class list would need to contain "collapsed" and aria-expanded set to "false".
This gets corrected though when the user clicks the button again, so I didn't opt to write that code.
If you are using bootstrap-vue and vue-router.
<script>
export default {
watch: {
'$route' () {
const element = document.querySelector("#nav-collapse");
let isShown = element.classList.contains("show");
if(isShown){
this.$root.$emit('bv::toggle::collapse', 'nav-collapse')
}
}
}
}
</script>
<template>
<b-navbar toggleable="lg" type="" class="p-0">
<b-navbar-toggle target="nav-collapse" id="navbar-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</b-navbar-toggle>
<b-collapse id="nav-collapse" is-nav>
<div class="nav-wrapper w-100">
<ul class="app-navigation__list">
<router-link to="/whatever" tag="li" exact class="app-navigation__list__item">
<a href="">
<span class="link-text">Home</span>
</a>
</router-link>
<router-link to="/whatever" tag="li" exact class="app-navigation__list__item">
<a href="">
<span class="link-text">About</span>
</a>
</router-link>
<router-link to="/whatever" tag="li" exact class="app-navigation__list__item">
<a href="">
<span class="link-text">Store</span>
</a>
</router-link>
</ul>
</div>
</b-collapse>
</b-navbar>
</template>
I know this is an old question but my answer might help someone.
do this in your router/index.js
const router = createRouter({}); //P.S I am using Vue3
then add this before each route entering like so.
router.beforeEach(() => {
$('.navbar-collapse').collapse('hide'); //Be sure to import jquery
});
Because this is such a simple task, I try to make it as simple as my simple website.
Vue.mixin((()=> {
let store = Vue.observable({
indexMenu: false,
})
return{
computed: {
menu: {
get() {
return store.indexMenu
},
set(val) {
store.indexMenu = val
}
}
},
watch: {
'$route' () {
this.menu = false
}
}
}})())
router.beforeEach((from,to,next) => {
$('.navbar-collapse').collapse('hide'); //Be sure to import jquery
next();
});
This works

navbar from Bootstrap to reactjs

I have programmed a navbar using Bootstrap and react. In order to obtain the functionality of bootstrap must be installed and bootstrap.js jquery.js. I just want to basically use the CSS file of bootstrap and the functionality of reactjs. Does it make sense to use Bootstrap with reactjs?
I need to realize with reactjs a little help to program the navigation.
Here the source of my header. I need help to programm the navbar in reactjs without bootstrap.js and jquery.min.js
import React from "react"
export class Header extends React.Component {
render() {
return (
<nav className="navbar-kwp-header navbar-default navbar-fixed-top">
<div className="container">
<div className="navbar-header">
<button type="button" className="navbar-toggle collapsed" data-toggle="collapse" data-target="#Navbar">
<span className="sr-only">Navigation ein- / ausblenden</span>
<span className="icon-bar"></span>
<span className="icon-bar"></span>
<span className="icon-bar"></span>
</button>
<a className="navbar-brand" href="#"><img src="images/logo.jpg" alt="" /></a>
</div>
<div id="Navbar" className="navbar-collapse collapse">
<ul className="nav navbar-nav">
<li>Home</li>
<li className="dropdown"><a className="dropdown" data-toggle="dropdown" role="button" aria-expanded="false">Service <span className="caret"></span></a>
<ul className="dropdown-menu" role="menu">
<li>Downloads</li>
<li>Glossar</li>
<li>Newsletter</li>
</ul>
</li>
</ul>
</div>
</div>
</nav>
);
}
}
You can manually code a state variable to handle the toggling of the navbar:
class App extends Component {
state = {
navCollapsed: true
}
_onToggleNav = () => {
this.setState({ navCollapsed: !this.state.navCollapsed })
}
render () {
const {navCollapsed} = this.state
return (
<nav className='navbar navbar-default'>
<div className='navbar-header'>
<a className='navbar-brand' href='/'>Your Brand</a>
<button
aria-expanded='false'
className='navbar-toggle collapsed'
onClick={this._onToggleNav}
type='button'
>
<span className='sr-only'>Toggle navigation</span>
<span className='icon-bar'></span>
<span className='icon-bar'></span>
<span className='icon-bar'></span>
</button>
</div>
<div
className={(navCollapsed ? 'collapse' : '') + ' navbar-collapse'}
>
<ul className='nav navbar-nav navbar-right'>
<li>
<a>About</a>
</li>
</ul>
</div>
</nav>
)
}
}
You can easily use bootstrap in your react components by using react-bootstrap package.
https://react-bootstrap.github.io/
This is an example with navbar which you want to use.
import React from "react"
import { Navbar, Nav, NavItem, NavDropdown, MenuItem } from 'react-bootstrap';
export class Header extends React.Component {
render() {
return (
<Navbar>
<Navbar.Header>
<Navbar.Brand>
React-Bootstrap
</Navbar.Brand>
</Navbar.Header>
<Nav>
<NavItem eventKey={1} href="#">Link</NavItem>
<NavItem eventKey={2} href="#">Link</NavItem>
<NavDropdown eventKey={3} title="Dropdown" id="basic-nav-dropdown">
<MenuItem eventKey={3.1}>Action</MenuItem>
<MenuItem eventKey={3.2}>Another action</MenuItem>
<MenuItem eventKey={3.3}>Something else here</MenuItem>
<MenuItem divider />
<MenuItem eventKey={3.3}>Separated link</MenuItem>
</NavDropdown>
</Nav>
</Navbar>
);
}
}

Categories