React and Tailwind CSS - switching sticky class on scroll - javascript

In my React page, there is an element, Block-2 positioned as sticky. When my scroll crosses the #block3 element, I would like to remove sticky class. When the user scrolls up and #block3 is visible in the scroll area again, the sticky class should be reapplied.
For Tailwind CSS, I'm not able to determine the correct way to do this, since the classnames fall under a direct element.
How can I handle this?
My React Page:
// Dependencies
import React from "react";
// Styles
import "./tailwind.output.css";
const App = () => {
const handleScroll = (event) => {
//how to achieve the goal?
console.log(event.currentTarget.scrollTop);
console.log(event.currentTarget.offsetHeight);
};
return (
<div
onScroll={handleScroll}
className="overflow-y-auto flex flex-wrap gap-4 h-screen border-solid border-4 border-indigo-600"
>
<div className="sticky bg-gray-500 top-0 z-30 h-[32rem] border-solid border-4 border-orange-600 ">
Block-2
</div>
<div
id="block3"
className="h-[22rem] border-solid border-4 border-green-600 "
>
It is a long established fact that a reader will be distracted by the
readable content of a page when looking at its layout. The point of
using Lorem Ipsum is that it has a more-or-less normal distribution of
letters, as opposed to using 'Content here, content here', making it
look like readable English. Many desktop publishing packages and web
page editors now use Lorem Ipsum as their default model text, and a
search for 'lorem ipsum' will uncover many web sites still in their
infancy. Various versions have evolved over the years, sometimes by
accident, sometimes on purpose (injected humour and the like).
</div>
<div className="h-[32rem] grow border-solid border-4 border-gray-600 ">
Block-3
</div>
<div>Last child</div>
</div>
);
};
export default App;
Demo:
Code Sandbox

Related

Append component to end of <p>

I'm trying to create a simple page footer in React and TailwindCSS where there is an emoji which shows a tooltip on hover. It looks great when the page is in desktop scaling, however when I test mobile scaling, the emoji doesn't stay appended to the end of the text, it is instead placed next to the whole component and reads as if it is in the middle of the text. Please see images below.
Does anyone know how I can essentially 'glue' this <Tooltip> component to the end of the <p> component?
The Tooltip component I am trying to use is from the Flowbite-React module: https://github.com/themesberg/flowbite-react/tree/main/src/lib/components/Tooltip
My code:
export default function Footer() {
return (
<div className="flex justify-center">
<div className="w-4/6">
....
<div className="flex w-full justify-center">
<p className="dark:text-gray-300 text-black mr-1">© Me test test test, 2022 - Built with ❤️ - Powered by</p>
<Tooltip content="Tooltip text">☕</Tooltip>
</div>
</div>
</div>
);
}
Desktop scaling:
Mobile scaling:
Thanks!
I've been unable to figure this out, so to work around it, I have duplicated the code, and reformatted one of the copies to display as I intended. One copy shows when the screen is 'small' or larger, and the newly formatted duplicate will show only on mobile devices.
<div className="w-full justify-center hidden sm:flex">
<p className="dark:text-gray-300 text-black mr-1 flex-0">© Me test test test, 2022 - Built with ❤️ - Powered by</p>
<Tooltip content="Tooltip text">☕</Tooltip>
</div>
<div className="w-full justify-center sm:hidden">
<p className="dark:text-gray-300 text-black mr-1 flex-0">© Me test test test, 2022 - Built with ❤️</p>
<div className="flex">
<p className="dark:text-gray-300 text-black mr-1 flex-0">Powered by</p>
<Tooltip content="Tooltip text">☕</Tooltip>
</div>
</div>
Result:

How to get component loaded state in Next.js?

I am working on a simple Pokedex that shows all pokemon, I am displaying all the cards by using a map function on an array of pokemon objects like this:
{pokemons.results.map((el, i) => {
return (
<div key={i} className="lg:w-1/4 md:w-1/2 p-4 w-full">
<Link href={"/"}>
<a className="block relative rounded-lg overflow-hidden hover:cursor-pointer hover:shadow-xl hover:shadow-poke-blue/50 p-4 bg-poke-blue group">
<img
alt="ecommerce"
className="object-cover object-center w-full block"
src={`https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/${el.url
.replace("https://pokeapi.co/api/v2/pokemon/", "")
.replace("/", "")}.png`}
height="400px"
onError={addDefaultSrc}
/>
<div className="mt-4">
<h2 className="group-hover:underline text-white text-center title-font text-lg font-medium uppercase">
{el.name}
</h2>
</div>
</a>
</Link>
</div>
);
})}
there is an image tag in the div of the card which displays the image of the pokemon like this:
I also added the functionality to search pokemon based on the name. The problem is as soon as the state changes the images don't change immediately which makes sense since the images don't get downloaded instantly. For a few minutes, it shows the image of the previously loaded pokemon image at the same place.
So I need help to show a loader for the time till all the images have loaded. I will use a loader that I already have I need to know how to get the event of all images loaded.
Thanks for reading!
There is an onLoad event on the img tag you should be able to just add a loaded property on each item in the local copy of the data, then just updated the loaded property onLoad and then make a derived loaded variable
const loaded = images().every(img => img.loaded)
Also #Chris G's message about the keys is entirely correct and should be followed, I was just answering the question

Scrollheight is not accurate for Nuxt app?

I have a very long (height) webpage and I'm using Puppeteer to get a PDF export of the page. It's about 30,000 px long.
I'm using document.documentElement.scrollHeight to try to determine dynamically the total height but it is returning 1018 when console.log and the PDF export is showing the same.
The 1018 is the viewport height so if I change the window it changes also.
Curious why this is?
The height is returning this way even in direct inspect > console.log and not using Puppeteer.
Any ideas why the height is not correct or how to fix this?
UPDATE:
This is my layout in nuxt
<template>
<div
id="my-app"
ref="layout"
class="flex flex-col h-screen overflow-hidden bg-gray-100 font-museo-sans"
>
<main class="overflow-y-auto">
<div class="max-w-7xl mx-auto py-5 xl:rounded-lg xl:shadow-md">
<Nuxt class="transition-opacity duration-200" />
</div>
</main>
</div>
</template>
The overflow-hidden and h-screen for some reason is necessary for my-app as if I remove it, the page does not allow for scroll.
I'm not quite understanding the behavior of this though. Why would overflow-hidden and setting a height to viewport allow the scrolling of the nuxt application?
In addition, I thought documentElement.scrollHeight returns overflow hidden but I guess it does not.

TailwindCSS won't extend the width of my VueJS application to full screen

I've put all my components under a div with flex-row, w-screen and content-center and for some reason when I go on reactive/mobile mode on the browser it takes about 2/3 of the screen and I can't get it to fill up the remaining screen space.
Here is the code for the View that holds all the components:
<template>
<div class="flex-row h-screen w-screen content-center bg-gray-700">
<div class="flex w-screen h-5/6 content-center" id="splash"> <Splash /></div>
<div class="flex bg-blue-800 w-screen h-5/6" id="skills"> <Skills /></div>
<div class="flex bg-green-700 w-screen h-5/6" id="projects"></div>
<div class="flex bg-pink-700 w-screen h-5/6" id="about"></div>
</div>
</template>
<script>
import Splash from '../components/Splash.vue';
import Skills from '../components/Skills.vue';
export default {
name: "Home",
components: {
Splash,
Skills
},
data: function() {
return {
}
}
}
</script>
and it ends up looking like this on mobile:
https://i.stack.imgur.com/Y6fLt.png (I put the background of the view's div container as gray to highlight how much is left of the screen)
Please help me out because I got no clue.. Thank you everyone!
You can use: w-full instead of w-screen
What you'll see in the docs and Tailwind examples, specifically in the component library of TailwindUI, is that they will create nested divs that each serve a specific purpose.
They'll have the outer div, parent container, and either apply margin/padding, then a nested div for a border, or the reverse. Then a div specifically for a flex row col, then inner divs for flex rows, etc. It's a nested structure. They don't try to use one div to accomplish too many things.
You should get familiar with Tailwind Play. You can learn a ton from this tool which is the official playground tool of Tailwind.
https://play.tailwindcss.com/
Also, check out tailwindui.com for their UI library. Some of their code, they show you, read it carefully. See the TailwindLabs Youtube channel. It is fantastic for learning basic and advanced TW.
Here's a basic strategy for full width on mobile and constrained to a breakpoint with padded content above.
<template>
<!-- outer container -->
<div class="container mx-auto sm:px-6 lg:px-8">
<!-- this would be your main flex container, through a border or anything -->
<div class="flex flex-col justify-start w-full h-screen">
<!-- now define your child flex containers, cols or rows or define CSS grid-->
<!-- you could put your nav or header here as you're in a flex col-->
<header />
<!-- now define your horizontal layout for Splash, Skills, etc.-->
<!-- or place your full height mobile content here. -->
<div
</div>
</div>
</template>
Getting layouts right are tricky. TailwindUI and Tailwind Labs have a lot of free content that can help you get the outer structure right so that you can focus on the inner content. Also, there are a lot of freely available Tailwind components that tackle this and other issues from which you can learn tips/tricks.
https://tailwindcomponents.com/search?query=layouts
Good luck!
Marcus

Can you modify NextJS mount element or add classes to __next div?

Long story short is I'm working on a project where I want to have the content "fill" the vertical space below the static header. I've done this in React with tailwind like this:
<body class="flex flex-col h-screen text-gray-600 work-sans leading-normal text-base tracking-normal">
<header class="flex h-18 bg-white shadow-md">
{/* header menu options */}
</header>
<div class="flex flex-1 h-full bg-gray-200 p-6">
{/* page content */}
</div>
But with NextJS it seems to put the mounting div (i.e. <div id="__next">) between the body and the wrest of the content. If I modify the CSS to give #__next { height: %100 } but that makes the fill not work correctly, it overflows. So it looks like this:
<body class="flex flex-col h-screen text-gray-600 work-sans leading-normal text-base tracking-normal">
<div id="__next">
<header class="flex h-18 bg-white shadow-md">
{/* header menu options */}
</header>
<div class="flex flex-1 h-full bg-gray-200 p-6">
{/* page content */}
</div>
</div>
Here are screenshots to visually see why the extra div is causing problems: https://imgur.com/a/dHRsDkY
The two possible options to solve this problem that theoretically might work are add classes to the #__next div or mount to body instead of the #__next div. Does anyone know how to achieve either of those?
Edit: Yes, I think I could change the layout to a fixed header and padding on top of the content element and that'd sidestep the problem and that may end up being the workaround I need but I'm still interested in knowing if either of the solutions I've mentioned are possible because if they aren't that's a technical limitation of NextJS that doesn't get much attention.
I resolved the issue by removing classes from the body and applying them to the #__next container div:
I used the approach in this example for using tailwind with Next.js.
Then I edited styles/index.css
#tailwind base;
/* Write your own custom base styles here */
/* #__next {
height: 100%;
} */
/* Start purging... */
#tailwind components;
/* Stop purging. */
html,
body {
#apply bg-gray-50 dark:bg-gray-900;
}
#__next {
#apply flex flex-col h-screen text-gray-600 leading-normal text-base tracking-normal;
}
/* Write your own custom component styles here */
.btn-blue {
#apply px-4 py-2 font-bold text-white bg-blue-500 rounded;
}
/* Start purging... */
#tailwind utilities;
/* Stop purging. */
/* Your own custom utilities */
As you said, the point is to add the classes to #__next instead of the body.
As you can see in the comments, it's important where to add the #apply instruction.
The important lines are:
#__next {
#apply flex flex-col h-screen text-gray-600 leading-normal text-base tracking-normal;
}
As you've asked in your question title, this is one way to add tailwind styles to the #__next container div.
The other solution is to set the classes after the component or page is loaded using the componentDidMount() lifecycle hook or the useEffect hook like this:
useEffect(() => {
document.querySelector("#__next").className =
"flex flex-col h-screen text-gray-600 leading-normal text-base tracking-normal";
}, []);
If you take a look at the Next.js documentation about Custom document you can see that the Main component and NextScript are the internals of Next.js and are required for the page to be properly rendered, and you can not change Next.js internals, so I think the solutions mentioned above are the best ways to add classes to the #__next container div.
There is no need to inject elements between or replace NextJS' technical root element (#__next). Just stretch all parent elements of your app's root element to take all the screen height with 100vh (vh is "vertical height", a unit which is 1% of the viewport height).
html, body, #__next, #app {
min-height: 100vh;
}
Your HTML might look like this:
<html>
<head><!-- head content --></head>
<body>
<div id="__next">
<div id="app"><!-- this is your app's top element --></div>
</div>
</body>
</html>
you can change your html or body tags by creating a custom document in ./pages/_document.js
import Document, { Html, Head, Main, NextScript } from 'next/document'
class CustomDocument extends Document {
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx)
return { ...initialProps }
}
render() {
return (
<Html>
<Head />
<body className="custom-class-name">
<Main />
<NextScript />
</body>
</Html>
)
}
}
export default CustomDocument
Take into account that these tags DO need to be included in your file: <Html>, <Head />, <Main /> and <NextScript />.
Here's the full documentation
In your case, that element with the __next id is just what the element renders, and it's required for the main app to be mounted on. So you can't remove it or edit it. I think you can add some css in _document.js that targets #__next, using the #apply directive from tailwind might be the way to go instead of editing the HTML of the element (since it doesn't seem to be possible)
I found updating the styles/globals.css to be the easiest way to achieve this.
/*
Extend the top most enclosing elements to entire height of the screen
to allow for the background image to fill the entire screen
*/
html,
body,
#__next {
height: 100%;
}
Note that that global.css has to be imported into _app.tsx

Categories

Resources