I have navigation in my app, one link has got attr to='/' and I have method onClick that is taking me to section about. When I am on main Page '/' it works, but when I am on different pages nav works but only returning me to main page but I want also to take me to section. I can do this after second click. How can i solve this issue ?
<li>
<NavLink to="/" activeClassName={window.pageYOffset > this.scroll && "active-main"} className="link-left" onClick={this.goToAbout}>
O Nas
</NavLink>
</li>
Method:
goToAbout = () => {
window.scrollTo(0, this.scroll);
};
ComponentDidMount:
this.aboutUs = document.getElementById("aboutUs");
this.scroll = this.aboutUs.offsetTop - 100;
You need to prevent the default behavior so your link won't go to "/" anymore, only scrolls
goToAbout = (event) => {
event.preventDefault();
window.scrollTo(0, this.scroll);
};
Related
I built tabbing functionality in my web app using Links and an Outlet with subroutes
const { currentTab } = useLoaderData();
function tabClassName(tab: string) {
if (currentTab == tab) { return "is-active"; }
return "";
}
return ([
<div className="tabs is-centered m-5 pr-5 pl-5">
<ul>
<li className={tabClassName("tab1")}><Link to="tab1">one</Link></li>
<li className={tabClassName("tab2")}><Link to="tab2">two</Link></li>
<li className={tabClassName("tab3")}><Link to="tab3">three</Link></li>
</ul>
</div>,
<Outlet></Outlet>
]);
The loader function supplies the component with the current subroute like this:
const parts = new URL(request.url).pathname.split("/");
const tab = parts.pop() || parts.pop() || ""; // get final part of path /settings/tab1 -> tab1
if (tab === "settings") {
return redirect("/settings/tab1");
}
return json<LoaderData>({ currentTab: tab });
When I click the links the correct route is loaded instantly. However, the css class is only applied after clicking the same link a second time. I found out that the loader function is only executed after the second click. How can i force remix to make a server request again? or should i use client side js for this? I cant use the NavLink component because of the way my css framework is structured.
Thanks!
I'm making a chrome extension using react. In my app I'm using the Rsuite Dropdown component. I'm using nested menus with lots of items, so on some of the menus the list of items in the dropdown extends past the screen. The issue is that I hover over the menu, the dropdown opens, it extends past the screen, the window scrolls to the bottom of the dropdown, my mouse is now shifted off of the menu selector so the dropdown closes and the window scrolls back up. My goal is to have the dropdown open without the window scrolling down to the bottom.
Gif of problem
My Code:
I just have one component in my App.js, which is just a dropdown of all my bookmarks
<Dropdown title="Bookmarks">
{bookmarks.map((mark) => {
return (mark.children === undefined ? <BookmarkItem Bookmark={mark}/> : <BookmarkFolder Bookmarks={mark}/>)
})}
</Dropdown>
For all the bookmarks, I check if its a folder or an actual bookmark, if its a folder, I insert a folder component (which does this exact thing recursively)
const BookmarkFolder = ({Bookmarks}) => {
return (
<Dropdown.Menu title={Bookmarks.title}>
{Bookmarks.children.map((mark) => {
return (mark.children === undefined ? <BookmarkItem Bookmark={mark}/> : <BookmarkFolder Bookmarks={mark}/>)
})}
</Dropdown.Menu>
);
};
If its a bookmark, I display the bookmark title as a dropdown item
/* Limit title length to 25 characters */
const limitLength = (title) => {
if (title.length > 25) {
return `${title.substring(0, 22)}...`;
}
else {
return title;
}
}
return (
<Dropdown.Item>
{limitLength(Bookmark.title)}
</Dropdown.Item>
);
I've been trying to programmatically scroll the page up when a bookmark item renders:
useEffect(() => {
window.setTimeout(() => window.scrollTo(0,0), 500);
}, []);
But this isn't working.
Help is greatly appreciated!
I'm building a conference website using three of these tabs (#speaker, #talks, #schedule). I think it is fair to want interactions between the tabs, here are a couple use cases that I cannot seem to solve.
From the #talks tab, I click on the bio hash - #johnsmith. This id exists within the page, but since I don't first switch tab to #speakers, nothing renders.
If I want to reference a specific talk and email someone the url: https://website.com#speaker_name the tabs won't open, and nothing but the tabs render.
The problem is compounded by the fact that when I click on an anchor tag href using a '#id', I must reload the page for it to fire.
I feel like there should be some way to pass a parameter when changing the tab or something... I'm in a tough spot because I'm rolling out code, but need this functionality badly.
Here is the actual open-source repo - https://github.com/kernelcon/website. The code I'm referencing can be found in src/pages/Agenda/.
Here is some example code.
Agenda.js
<Tabs defaultTab={this.state.defaultTab}
onChange={(tabId) => { this.changeTab(tabId) }}
vertical={vert}>
<TabList vertical>
<Tab tabFor="speakers">Speakers</Tab>
<Tab tabFor="talks">Talks</Tab>
<span>
<TabPanel tabId="speakers">
<Speakers />
</TabPanel>
<TabPanel tabId="talks">
<Talks />
</TabPanel>
</span>
</Tabs>
Talks.js
changeTab(id) {
window.location.reload(false);
}
getTalks() {
// Order Alphabetically
const talksOrdered = speakerConfig.sort((a,b) => (a.title > b.title) ? 1 : ((b.title > a.title) ? -1 : 0));
const talks = talksOrdered.map((ele, idx) => {
const twitterUrl = ele.twitter.replace('#', '');
return (
<div id={ele.talk_id}
key={idx}
className='single-talk'>
<div className='talk-title'>{ele.title}</div>
<div className='talk-sub-title'>
<div className='speaker-name'>
<a onClick={() => {this.changeTab(ele.speaker_id)}}
href={`#${ele.speaker_id}`}>{ele.speaker}</a>
</div>
...
I ended up accomplishing this by sending #tab_title/speaker_name, then adding a componentWillMount lifecycle method and function in the main tab file like below.
componentWillMount() {
const defaultTab = this.props.location.hash ? this.props.location.hash.split('#')[1] : 'schedule';
this.setState({
defaultTab: defaultTab
});
this.handleHashChange();
window.addEventListener('hashchange', this.handleHashChange);
}
handleHashChange = () => {
// given `#speakers/dave` now you have tabName='speakers', speakerHash='dave'
const [tabName, speakerHash] = window.location.hash.replace('#', '').split('/');
const tabNamesToWatchFor = [
'schedule',
'speakers'
];
if (tabNamesToWatchFor.includes(tabName)) {
this.setState({
defaultTab: tabName,
// pass this.state.speakerHash to <Speakers/> and use this for scrollIntoView in componentDidMount
speakerHash: speakerHash
});
}
}
Next, I went to the individual tab (in this case Speakers.js) and added a componentDidMount and componentDidUpdate method to help scroll to the speaker itself.
componentDidMount() {
this.handleScrollToSpeaker(this.props.speakerHash);
}
componentDidUpdate(prevProps) {
if (prevProps.speakerHash !== this.props.speakerHash) {
this.handleScrollToSpeaker(this.props.speakerHash);
}
}
handleScrollToSpeaker = hash => {
window.setTimeout(() => {
const ele = document.querySelector(`#${hash}`);
if (ele) {
ele.scrollIntoView({ block: 'start', behavior: 'smooth' });
}
}, 500)
}
I am having an issue where the "right click" on the links that are created with React Router Link tags.
I want to be able to right click on those links and select "Open Link in New Tab" option.
Could anyone please help me with this issue?
Here is my code:
redirectUrl = (e) => {
let url = e.currentTarget.getAttribute("dataattrurl");
browserHistory.push({
pathname : url,
query : '',
state : {}
});
}
const listItems = readingHistory.map((item, index) => {
return (
<li key={index} role="listitem">
<Link className="linkPointer" onClick={this.redirectUrl} dataattrurl={`/document/${item.resId}`} onContextMenu={this.contextMenu}>
{item.title}
</Link>
</li>
);
});
The problem is that you are handling the click event with an onClick rather than with an href (to for Link), so the browser does not register this as a link to another page but rather an element with a click event listener.
What you should do is make use of the to attribute of Link, which is basically the href for a regular a tag. See the docs here: https://reacttraining.com/react-router/web/api/Link
This will also result in much cleaner code. All your code can be reduced to:
const listItems = readingHistory.map((item, index) => {
return (
<li key={ index } role="listitem">
<Link className="linkPointer" to={ `/document/${item.resId}` } onContextMenu={ this.contextMenu }>
{ item.title }
</Link>
</li>
);
});
So the redirectUrl can be removed entirely.
I'm using Redux in my app, inside a Component I want to scroll to an specific div tag when a change in the store happens.
I have the Redux part working so it triggers the componentDidUpdate() method (I routed to this compoennt view already).
The problem as far as I can tell, is that the method scrollIntoView() doesn't work properly cos componentDidUpdate() has a default behavior that scrolls to the top overwriting the scrollIntoView().
To work-around it I wrapped the function calling scrollIntoView() in a setTimeout to ensure that happens afeterwards.
What I would like to do is to call a preventDefault() or any other more elegant solution but I can't find where to get the event triggering the 'scrollTop'
I looked through the Doc here: https://facebook.github.io/react/docs/react-component.html#componentdidupdate
and the params passed in this function are componentDidUpdate(prevProps, prevState) ,since there is no event I don't know how to call preventDefault()
I've followd this Docs: https://facebook.github.io/react/docs/refs-and-the-dom.html
And tried different approaches people suggested here: How can I scroll a div to be visible in ReactJS?
Nothing worked though
Here is my code if anyone has any tip for me, thanks
class PhotoContainer extends React.Component {
componentDidUpdate(){
setTimeout(() => {
this.focusDiv();
}, 500);
}
focusDiv(){
var scrolling = this.theDiv;
scrolling.scrollIntoView();
}
render() {
const totalList = [];
for(let i = 0; i < 300; i += 1) {
totalList.push(
<div key={i}>{`hello ${i}`}</div>
);
}
return (
<div >
{totalList}
<div ref={(el) => this.theDiv = el}>this is the div I'm trying to scroll to</div>
</div>
)
};
}
Ok it's been a while but I got it working in another project without the setTimeOut function so I wanted to answer this question.
Since Redux pass the new updates through props, I used the componentWillRecieveProps() method instead of componentDidUpdate() , this allowes you a better control over the updated properties and works as expected with the scrollIntoView() function.
class PhotoContainer extends React.Component {
componentWillReceiveProps(newProps) {
if (
this.props.navigation.sectionSelected !==
newProps.navigation.sectionSelected &&
newProps.navigation.sectionSelected !== ""
) {
this.focusDiv(newProps.navigation.sectionSelected);
}
}
focusDiv(section){
var scrolling = this[section]; //section would be 'theDiv' in this example
scrolling.scrollIntoView({ block: "start", behavior: "smooth" });//corrected typo
}
render() {
const totalList = [];
for(let i = 0; i < 300; i += 1) {
totalList.push(
<div key={i}>{`hello ${i}`}</div>
);
}
return (
<div >
{totalList}
<div ref={(el) => this.theDiv = el}>
this is the div I am trying to scroll to
</div>
</div>
)
};
}
I also struggled with scrolling to the bottom of a list in react that's responding to a change in a redux store and I happened upon this and a few other stackoverflow articles related to scrolling. In case you also land on this question as well there are a few ways this could be a problem. My scenario was that I wanted a 'loading' spinner screen while the list was rendering. Here are a few wrong ways to do this:
When loading = true, render spinner, otherwise render list.
{loading ?
<Spinner />
:
<List />
}
as stated above this doesn't work because the list you might want to scroll to the bottom of isn't rendered yet.
When loading set the display to block for the spinner and none for the list. When done loading, reverse the display.
<div style={{display: loading ? 'block' : 'none'>
<Spinner />
</div>
<div style={{display: loading ? 'none' : 'block'>
<List />
</div>
This doesn't work either since the list you want to scroll to the bottom of isn't actually being displayed likely when you call the scroll.
The better approach for the above scenario is to use a loading that acts as an overlay to the component. This way both the spinner and list are rendered and displayed, the scroll happens, and when the loading is complete, the spinner can be de-rendered or set to be invisible.