I am currently making a chat application and having trouble making the div scroll to the bottom when a new chat message is added, I used the css overflow-y:auto to make it be able to scroll but can't figure out how to make it always scroll to the bottom when a new chat is loaded.
Here is my app.js file:
import "./App.css";
import React, { useEffect, useState ,useRef} from "react";
import io from 'socket.io-client';
let socket;
const CONNECTION_PORT = "localhost:3002/";
function App() {
//before login
const [logged, setLogged] = useState(false);
const [name,setName] = useState("");
const [room,setRoom] = useState("");
//after login
const [message,setMessage] = useState("");
const [messageList,setMessageList] = useState([]);
const ref = useRef();
useEffect(()=>{
socket = io(CONNECTION_PORT,{transports: ['websocket', 'polling', 'flashsocket']});
},[])
useEffect(()=>{
socket.on('recieve_message', (data)=>{
setMessageList([...messageList,data])
})
});
// useEffect(()=>{
// const objDiv = document.getElementById('scroll');
// objDiv.scrollTop = objDiv.scrollHeight;
// },[message])
function connectRoom(){
socket.emit('join', room)
setLogged(true);
}
function sendMessage(e){
let messageContent = {
room:room,
content:{
author:name,
message:message
}
}
socket.emit('send_message',messageContent)
setMessageList([...messageList,messageContent.content])
setMessage("");
}
return (
<div className="App">
{!logged?(
<div className="background">
<div className="box">
<span className="text-center">login</span>
<div className="input-container">
<input type="text" required="" onChange={e=> setName(e.target.value)}/>
<label>Full Name</label>
</div>
<div className="input-container">
<input type="text" required="" onChange={ e=> setRoom(e.target.value)}/>
<label>Room</label>
</div>
<button type="button" className="btn" onClick={connectRoom}>submit</button>
</div>
</div>
):(
<>
<div className="chatContainer">
<div className="messages" id="scroll">
{messageList.map((val,key)=>{
return (
<div className="messageContainer" id={val.author == name ? "You" : "Other"}>
<div className="messageIndividual">
{val.message}
</div>
<h1> {val.author}</h1>
</div>
);
})}
</div>
<div className="messageInputs">
<input type="text" placeholder="Type Here" onChange={ e=> setMessage(e.target.value)}/>
<button type="submit" onClick={sendMessage}> Send</button>
</div>
</div>
</>
)}
</div>
);
}
export default App;
here is my css file:
body{
margin: 0px;
padding: 0px;
}
.App{
margin: 0px;
padding: 0px;
display: grid;
place-items: center;
height: 100vh;
background-color: #2a2730;
}
.background{
margin: 0px;
padding: 0px;
width: 100%;
height: 100vh;
background-color: #e74c3c;
}
.text-center{
color:#fff;
text-transform:uppercase;
font-size: 23px;
margin: -50px 0 80px 0;
display: block;
text-align: center;
}
.box{
position:absolute;
left:50%;
top:50%;
transform: translate(-50%,-50%);
background-color: rgba(0, 0, 0, 0.89);
border-radius:3px;
padding:70px 100px;
}
.input-container{
position:relative;
margin-bottom:25px;
}
.input-container label{
position:absolute;
top:0px;
left:0px;
font-size:16px;
color:#fff;
pointer-event:none;
transition: all 0.5s ease-in-out;
}
.input-container input{
border:0;
border-bottom:1px solid #555;
background:transparent;
width:100%;
padding:8px 0 5px 0;
font-size:16px;
color:#fff;
}
.input-container input:focus{
border:none;
outline:none;
border-bottom:1px solid #e74c3c;
}
.btn{
color:#fff;
background-color:#e74c3c;
outline: none;
border: 0;
color: #fff;
padding:10px 20px;
text-transform:uppercase;
margin-top:50px;
border-radius:2px;
cursor:pointer;
position:relative;
}
/*.btn:after{
content:"";
position:absolute;
background:rgba(0,0,0,0.50);
top:0;
right:0;
width:100%;
height:100%;
}*/
.input-container input:focus ~ label,
.input-container input:valid ~ label{
top:-12px;
font-size:12px;
}
.chatContainer {
width: 600px;
height: 350px;
border: 5px solid #0091ff;
border-radius: 10px;
display: flex;
flex-direction: column;
}
.chatContainer .messages {
flex: 80%;
width: 100%;
padding-left: 20px;
overflow-y: scroll;
/* flex-direction: column-reverse; */
}
.chatContainer .messageInputs {
flex: 20%;
width: 100%;
display: flex;
flex-direction: row;
}
.chatContainer .messageInputs input{
flex: 80%;
height: calc(100% -5px);
border: none;
border-top: 5px solid #0091ff;
padding-left: 20px;
font-size: 20px;
}
.chatContainer .messageInputs button{
flex: 20%;
height: 100%;
background-color: #0091ff;
border: none;
color: white;
font-size: 18px;
}
.messageContainer {
display: flex;
flex-direction: column;
width: calc(100% - 30px);
height: auto;
}
.messageContainer h1{
color: white;
font-family: Arial, Helvetica, sans-serif;
font-weight: 300;
font-size: 17px;
}
#You{
align-items: flex-end;
}
#Other {
align-items: flex-start;
}
#You .messageIndividual {
background-color: #5ff064;
color: black;
}
.messageIndividual {
width: 200px;
height: auto;
border-radius: 10px;
display: grid;
place-items: center;
background-color: #0091ff;
opacity: 0.9;
color: white;
font-family: Arial, Helvetica, sans-serif;
margin-right: 10px;
margin-top: 20px;
word-break: break-all;
white-space: pre-wrap;
padding: 4px;
}
here is what the chat application looks like:
You can add a span with ref bellow messages list that will be scroll into it when new messages are received.
Try this example :
useEffect :
useEffect(() => {
scrollSpan.current.scrollIntoView({ behavior: "smooth" });
}, [messageList]);
useRef :
const scrollSpan= useRef();
JSX :
<div className="messages" id="scroll">
{messageList.map((val,key)=>{
return (
<div className="messageContainer" id={val.author == name ?
"You" : "Other"}>
<div className="messageIndividual">
{val.message}
</div>
<h1> {val.author}</h1>
</div>
);
})}
<span ref={scrollSpan}></span>
</div>
On your 'messageContainer' div, create a reference using the 'useRef' React hook.
...
const container = useRef();
...
..
return (
...
<div class="messageContainer" ref={ container }>...</div>
);
and use one more useEffect as follows:
useEffect(() => {
container.current.scrollTop = chatBoxRef.current.scrollHeight;
}, [messageList]);
Some improvements(!important) to your code, which could prevent memory leaks in your app:
-so you're doing this:
useEffect(()=>{
socket.on('recieve_message', (data)=>{
setMessageList([...messageList,data])
})
});
This causes a new 'receive message' event to get registered to the socket every time your component re-renders. You don't want that. You want this event to get registered just once. So do as follows:
useEffect(()=>{
socket.on('recieve_message', (data)=>{
setMessageList(prevMessageList => [...prevMessageList, data]);
})
}, []);
Note the change in setMessageList.(Look up why I did that, it is related to closures since this useEffect now runs only on the first render).
Related
Actually, I have two questions
The First one is that Why is the following code using styled-components not working, I removed node modules, installed it again globally etc and it's not working. The output render is blank.
Am I using the Pseudo classes correctly in styled components If not please show me
I would really appreciate it if you could show me through the code I have.
I will List my Js file plus the desired CSS below,
I only want the CSS through styled-components
import React, { useRef} from "react";
import ReactDOM from 'react-dom/client';
import Home from "../Dashboard/Home";
import App from '../../App';
import Bye from "./Login"
import styled from "styled-components"
function Register(){
const name=useRef()
const email=useRef()
const password=useRef()
const root = ReactDOM.createRoot(document.getElementById('root'));
const handleClick=()=>{
if(name.current.value&&email.current.value&&password.current.value)
{
localStorage.setItem("name",name.current.value)
localStorage.setItem("email",email.current.value)
localStorage.setItem("password",password.current.value)
localStorage.setItem("signUp",email.current.value)
alert("Account created successfully!!")
root.render(
<React.StrictMode>
<Home/>
</React.StrictMode>
);
}
}
const goHome=()=>{
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
}
const handleSignIn=()=>{
root.render(
<React.StrictMode>
<Bye />
</React.StrictMode>
);
}
const Body = styled.div`
margin: 0;
padding: 0;
font-family: 'Poppins', sans-serif;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background: black;
`
const Box = styled.div`
position: relative;
width: 600px;
height: 540px;
background: #1c1c1c;
border-radius: 8px;
overflow: hidden;
&:before{
content: "";
position: absolute;
top: -50px;
left: -50px;
width: 600px;
height: 440px;
transform-origin: bottom right;
background: linear-gradient(0deg, transparent, transparent, #45f3ff, #45f3ff);
animation: animate 6s linear infinite
}
`
const Title = styled.h2`
align-items: center;
color: #45f3ff;
font-size: 25px;
font-weight: 500;
`
return(
<Body>
<Box>
<div className="form">
<Title>Hello Lets Get you Started</Title>
<div className="inputBox">
<input type="text"required="required" ref={name}/>
<span>Your Full Name: </span>
<i></i>
</div>
<div className="inputBox">
<input type="text"required="required" ref={email}/>
<span>Your Email: </span>
<i></i>
</div>
<div className="inputBox">
<input type="password" required="required" ref={password}/>
<span> Your Password: </span>
<i></i>
</div>
<div class="Links">
<button className="btn-2 btn" onClick={handleSignIn}> Sign In</button>
<button className="btn-3 btn" onClick={goHome}>Return Home</button>
</div>
<button type="submit" className="btn-1" onClick={handleClick}>Sign Up </button>
</div>
</Box>
</Body>
)
}
export default Register;
thats my JS file (not jsx)
below is the desired Css
#import url('https://fonts.googleapis.com/css2?family=Poppins:wght#300;400;500;600;700,800,900,&display=swap');
.body{
margin: 0;
padding: 0;
font-family: 'Poppins', sans-serif;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background: black;
}
.box{
position: relative;
width: 600px;
height: 540px;
background: #1c1c1c;
border-radius: 8px;
overflow: hidden;
}
.title{
align-items: center;
color: #45f3ff;
font-size: 25px;
font-weight: 500;
}
.box::before{
content: "";
position: absolute;
top: -50px;
left: -50px;
width: 600px;
height: 440px;
transform-origin: bottom right;
background: linear-gradient(0deg, transparent, transparent, #45f3ff, #45f3ff);
animation: animate 6s linear infinite
}
.box::after{
content: "";
position: absolute;
top: -50px;
left: -50px;
width: 600px;
height: 440px;
background: linear-gradient(0deg, transparent, #45f3ff, #45f3ff);
transform-origin: bottom right;
animation: animate 6s linear infinite;
animation-delay: -3s;
}
#keyframes animate{
0%{
transform: rotate(0deg)
}
100%{
transform: rotate(360deg)
}
}
.form{
position: absolute;
inset: 2px;
border-radius: 8px;
background: #28292d;
z-index: 10;
padding: 50px 40px;
display: flex;
flex-direction: column;
}
.inputBox span{
display: flex;
justify-content: space-between;
margin-right: 55px;
margin-top: -60px;
font-size: 18px;
font-weight: 500;
left: 0;
padding: 20px 10px 10px;
pointer-events: none;
letter-spacing: 0.05em;
transition: 0.5s
}
.inputBox{
position: relative;
width: 300px;
margin-top: 35px;
}
.inputBox input{
position: relative;
width: 165%;
padding: 20px 10px 10px;
background: transparent;
border: none;
text-align: left;
font-weight: 500;
outline: none;
color: black;
font-size: 1em;
letter-spacing: 0.05em;
font-size: 20px;
z-index: 10;
}
.btn-1{
margin-top: 30px;
pointer-events: auto;
cursor: pointer;
}
.Links{
margin-top: 25px;
}
.btn-2{
display: flex;
align-items: left;
margin-top: -10px;
font-size: 1.25em;
border: none;
outline: none;
background: none;
padding: 0;
color: #8f8f8f;
cursor: pointer;
}
.Links button:hover
{
color: #45f3ff;
}
.btn-3{
display: flex;
flex-direction: column;
margin-left: 25rem;
margin-top: -20px;
font-size: 1.25em;
cursor: pointer;
border: none;
outline: none;
background: none;
padding: 0;
color: #8f8f8f;
}
.inputBox input:valid ~ span,
.inputBox input:focus ~ span
{
color: #45f3ff;
transform: translateY(-44px);
font-size: 1.25em;
}
.inputBox i{
position: absolute;
left: 0;
bottom: -7px;
width: 170%;
height: 2px;
background: #45f3ff;
border-radius: 4px;
transition: 0.5s;
pointer-events: none;
z-index: 9;
}
.inputBox input:valid ~ i,
.inputBox input:focus ~ i
{
height: 55px;
width: 170%;
top: 2px;
}
.btn-1{
border: none;
outline: none;
background: #45f3ff;
padding: 11px 25px;
width: 100px;
margin-top: 10px;
border-radius: 4px;
font-weight: 600;
cursor: pointer;
}
.btn-1:active{
opacity: 0.8
}
Edit: There seems to be that the code works now randomly but i didnt even put all my css and its giving me the exact output i want weird?
It is important to define your styled components outside of the main react component, otherwise it will be recreated on every single render pass. Defining a styled component within the react component will prevent caching and drastically slow down rendering speed, and should be avoided.
import React, { useRef } from 'react';
import ReactDOM from 'react-dom/client';
import styled from 'styled-components';
const Body = styled.div`
margin: 0;
padding: 0;
font-family: 'Poppins', sans-serif;
display: flex;
justify-content: center;
align-items: center;
`;
const Box = styled.div`
position: relative;
width: 600px;
height: 540px;
border-radius: 8px;
overflow: hidden;
&:before{
content: "";
position: absolute;
top: -50px;
left: -50px;
width: 600px;
height: 440px;
transform-origin: bottom right;
animation: animate 6s linear infinite
}
`;
const Title = styled.h2`
align-items: center;
font-size: 55px;
font-weight: 600;
color: red;
`;
function App() {
const name = useRef();
const email = useRef();
const password = useRef();
const handleClick = () => {
if (name.current.value && email.current.value && password.current.value) {
localStorage.setItem('name', name.current.value);
localStorage.setItem('email', email.current.value);
localStorage.setItem('password', password.current.value);
localStorage.setItem('signUp', email.current.value);
alert('Account created successfully!!');
}
};
const goHome = () => {};
const handleSignIn = () => {
root.render();
};
return (
<Body>
<Box>
<div className='form'>
<Title>Hello Lets Get you Started</Title>
<div className='inputBox'>
<input type='text' required='required' ref={name} />
<span>Your Full Nameeee: </span>
</div>
<div className='input-box'>
<input type='text' required='required' ref={email} />
<span>Your Email: </span>
<i></i>
</div>
<div className='input-box'>
<input type='password' required='required' ref={password} />
<span> Your Password: </span>
</div>
<div className='Links'>
<button className='btn-2 btn' onClick={handleSignIn}>
{' '}
Sign In
</button>
<button className='btn-3 btn' onClick={goHome}>
Return Home
</button>
</div>
<button type='submit' className='btn-1' onClick={handleClick}>
Sign Up{' '}
</button>
</div>
</Box>
</Body>
);
}
export { App };
Here is my solution. Keep styled component outside of Register component.
import React, { useRef } from 'react';
import ReactDOM from 'react-dom/client';
import Home from '../Dashboard/Home';
import App from '../../App';
import Bye from './Login';
import styled from 'styled-components';
const Body = styled.div`
margin: 0;
padding: 0;
font-family: 'Poppins', sans-serif;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background: black;
`;
const Box = styled.div`
position: relative;
width: 600px;
height: 540px;
background: #1c1c1c;
border-radius: 8px;
overflow: hidden;
&:before {
content: '';
position: absolute;
top: -50px;
left: -50px;
width: 600px;
height: 440px;
transform-origin: bottom right;
background: linear-gradient(
0deg,
transparent,
transparent,
#45f3ff,
#45f3ff
);
animation: animate 6s linear infinite;
}
`;
const Title = styled.h2`
align-items: center;
color: #45f3ff;
font-size: 25px;
font-weight: 500;
`;
function Register() {
const name = useRef();
const email = useRef();
const password = useRef();
const root = ReactDOM.createRoot(document.getElementById('root'));
const handleClick = () => {
if (name.current.value && email.current.value && password.current.value) {
localStorage.setItem('name', name.current.value);
localStorage.setItem('email', email.current.value);
localStorage.setItem('password', password.current.value);
localStorage.setItem('signUp', email.current.value);
alert('Account created successfully!!');
root.render(
<React.StrictMode>
<Home />
</React.StrictMode>
);
}
};
const goHome = () => {
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
};
const handleSignIn = () => {
root.render(
<React.StrictMode>
<Bye />
</React.StrictMode>
);
};
return (
<Body>
<Box>
<div className="form">
<Title>Hello Lets Get you Started</Title>
<div className="inputBox">
<input type="text" required="required" ref={name} />
<span>Your Full Name: </span>
<i></i>
</div>
<div className="inputBox">
<input type="text" required="required" ref={email} />
<span>Your Email: </span>
<i></i>
</div>
<div className="inputBox">
<input type="password" required="required" ref={password} />
<span> Your Password: </span>
<i></i>
</div>
<div class="Links">
<button className="btn-2 btn" onClick={handleSignIn}>
{' '}
Sign In
</button>
<button className="btn-3 btn" onClick={goHome}>
Return Home
</button>
</div>
<button type="submit" className="btn-1" onClick={handleClick}>
Sign Up{' '}
</button>
</div>
</Box>
</Body>
);
}
export default Register;
Hope this help you.
when user clicks on any card which contains the data (the data is getting from backend api GET method)that data should bind-up or displays on a pop-card ,How to do this thing .in my case i have two components one is DisplayNotes.vue and another one is UpdateNotes.vue .when ever user clicks on any displayed cards that data is bind to the updateNotes card(popup) .How to pass that data to the pop-up card ,please help me to fix this issue.
[DisplayNotes.vue]
<template>
<div class="carddisplay-section" >
<div v-for="note in notes" :key="note.data" id="blur" class="container note">
<div #click="toggle()" class="card-content">
<h5>{{note.title}}</h5>
<p>{{note.body}}</p>
</div>
<div class="import-icons">
<icons class="imported-icons note-icons" />
<button v-if="flag" class="card-button" type="button" #click="handlesubmit();Togglebtn();">Close</button>
</div>
</div>
<div id="popup">
<UpdateNotes/>
</div>
</div>
</template>
<script>
import service from '../service/User'
import icons from './icons'
import UpdateNotes from './UpdateNotes.vue'
export default {
name: 'DisplayNotes',
components: {
icons,UpdateNotes
},
data() {
return {
flag: true,
notes: [{
id: 1,
title: 'Fundoo',
body: 'unlimited notes..'
}, ],
}
},
methods: {
Togglebtn() {
this.flag = !this.flag;
},
async handlesubmit() {
service.userDisplayNotes().then(response => {
this.notes.push(...response.data);
})
},
toggle(){
var blur=document.getElementById('blur');
blur.classList.toggle('active');
var popup=document.getElementById('popup');
popup.classList.toggle('active');
}
}
}
</script>
<style lang="scss">
.carddisplay-section{
display: flex;
align-items: flex-start;
flex-wrap: wrap;
align-content: space-around;
gap: 10px;
}
.container {
height: 180px;
background: #fff;
padding: 7px;
border-radius: 7px;
box-shadow: 5px 5px 10px #e0dede;
margin-top: 5%;
margin-left: 18%;
margin-right: -15%;
float: left;
width: 22%;
}
.card-content {
h5 {
font-size: 20px;
font-weight: 400;
font-family: 'Times New Roman', Times, serif;
padding-left: 10px;
}
p {
font-size: 18px;
width: 90%;
height: 60px;
font-family: 'Times New Roman', Times, serif;
width: 100%;
border: none;
padding: 7.5px 10px;
outline: none;
}
}
.card-button {
float: right;
margin-top: -1px;
margin-left: 240px;
font-size: 14px;
border: none;
background: none;
font-family: 'Times New Roman', Times, serif;
}
.note-icons {
visibility: hidden;
}
.note {
&:hover {
.note-icons {
visibility: visible;
}
}
}
.imported-icons {
margin-top: 10%;
}
#blur.active{
filter:blur(0.5px);
// pointer-events: none;
// user-select: none;
}
#popup{
position: fixed;
top:40%;
left:50%;
transform: translate(-50%,-50%);
visibility: hidden;
opacity: 0;
transition: 0.5s;
}
#popup.active{
visibility: visible;
opacity: 1;
transition: 0.5s;
}
.card-content p,h5 {
word-break: break-word;
overflow-wrap: break-word;
}
</style>
This is my updateNotes.vue(popup card)
[UpdateNotes.vue]
<template>
<div class="update" >
<form class="update-note" #submit.prevent="handlesubmit" autocomplete="off">
<input name="title" v-model="title" placeholder="Title" />
<textarea name="content" v-model="body" style="resize: none" placeholder="Take a note..." rows="3"></textarea>
<div class="btm-icons">
<icons />
<button id="btn-section" type="submit" >Close</button>
</div>
</form>
</div>
</template>
<script>
import icons from './icons.vue'
export default{
components:{icons},
methods:{
flip() {
this.flag = !this.flag;
},
}
}
</script>
<style scoped>
.update {
padding-top: 0%;
}
.update-note {
position: relative;
width: 550px;
max-width: 100%;
margin: 152px auto;
margin-right: 80%;
background: rgb(255, 255, 255);
padding: 15px;
border-radius: 5px;
box-shadow: 0 1px 5px #ccc;
}
.update-note input {
width: 100%;
max-width: 100%;
border: none;
padding: 4px;
outline: none;
font-size: 1.2em;
}
textarea {
width: 100%;
max-width: 100%;
border: none;
padding: 4px;
outline: none;
font-size: 1.2em;
}
button {
border: none;
background: transparent;
font-weight: 500;
float: right;
margin-top: -5%;
cursor: pointer;
}
</style>
You simply bind the data to want to send to the child component as a prop. In your case you put in on the component like this:
<UpdateNotes :body="note.body"></UpdateNotes>
Then, you would have to define the prop in the other component unless you want to get it from $refs:
<script>
export default{
props: {
body: String
}
}
</script>
Note that it is not recommended to alter props, so you might want to duplicate the body prop in your data() section. Then, you could bind that new (now local) data property to the textarea. In the beforeDestroy hook of your component, you then can call a method that compares the local property to the prop and use this.$emit to inform the parent component of any changes.
I have this part of component:
<div id="container">
<span id="magnify">🔍</span>
<input id="search" />
<span id="user">👤</span>
<span
#click="dialog = true"
id="buttonMenuHamburger">Ⓜ️</span>
</div>
<transition name="fade">
<div
id="menuOverlay"
v-if="dialog"
>
<div
class="fondOverlay"
#click="dialog = false">
</div>
<ul class="contenuMenuOverlay">
<li>
<router-link to="/home">
➕ Home
</router-link>
</li>
</ul>
</div>
</transition>
<script>
import {
ref,
} from 'vue';
import { useRouter } from 'vue-router';
export default {
name: 'SearchBar',
setup() {
const router = useRouter();
const dialog = ref(false);
router.beforeEach((to, from, next) => {
dialog.value = false;
next();
});
return {
dialog,
};
},
};
</script>
<style scoped>
a {
color: black;
}
#menuOverlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
#menuOverlay .fondOverlay {
background-color: rgba(10, 10, 10, 0.5);
height: 100%;
width: 100%;
position: absolute;
z-index: 99;
}
#menuOverlay .contenuMenuOverlay {
padding: 20px;
margin: 20px;
border: 2px solid white;
text-align: left;
font-size: 2em;
font-variant: small-caps;
border: 2px solid white;
border-radius: 30px;
background-color: rgba(255, 255, 255, 0.5);
z-index: 100;
}
ul {
list-style-type: none;
font-family: "Champagne & Limousines";
font-variant: small-caps;
}
#container {
margin-bottom: 10px;
}
#container span {
padding-top: 7px;
cursor: pointer;
position: absolute;
}
#magnify {
padding-left: 5px;
min-width: 30px;
}
#user {
margin-left: -60px;
}
#buttonMenuHamburger {
margin-left: -30px;
}
#search {
background: transparent;
text-align: center;
color: white;
font-weight: bold;
font-size: 24px;
padding: 0 80px;
height: 30px;
width: 25%;
border: 2px solid white;
border-radius: 30px;
font-family: 'Caviar Dream';
transition: width 0.2s;
outline: none;
}
#search:focus {
width: 50%;
}
#search:before { content: 'Some'; }
</style>
When I click on the Ⓜ️, the dialog value changes correctly. But if I have the focus on the input, when I click on Ⓜ️ it just loses the focus on the input. I have to click again on the Ⓜ️ to get the dialog value set to true.
Is there something I'm missing?
Thanks in advance.
EDIT: it seems to come from the width change...
I might be misunderstanding your problem but I can't replicate the issue.
const { watchEffect, ref, onMounted, nextTick } = Vue;
Vue.createApp({
setup() {
const dialog = ref(false);
const input = ref(null);
// log when `dialog` is changed
watchEffect(() => console.log(dialog.value));
// focus on `input` from the beginning
onMounted(() => nextTick(() => input.value.focus()));
return { dialog, input };
},
}).mount('#app');
<script src="https://unpkg.com/vue#3.0.7/dist/vue.global.js"></script>
<div id="app">
<span id="magnify">🔍</span>
<input id="search" ref="input" />
<span id="user">👤</span>
<transition name="fade">
<span
#click="dialog = true"
id="buttonMenuHamburger">Ⓜ️</span>
</transition>
</div>
I have an input field that has a search icon positioned inside of it using the background property and every time I type in it the cursor sticks in the beginning like shown in the picture:
https://imgur.com/ocJRux8
How can I position the cursor after the search icon? I tried setting the padding-left and the text-indent but that also moves the placeholder which is not desirable.
Here is my input field:
import React, { Component } from "react";
import Spinner from '../../components/Spinner/Spinner.jsx';
import { Link } from 'react-router-dom';
import axios from "axios";
class Search extends Component {
state = {
searchResults: [],
isLoading: false
}
getSearchQuery = (event) => {
const SEARCH_RESULTS_ENDPOINT = process.env.REACT_APP_SEARCH_ENDPOINT;
const queryString = document.querySelector(
".search-input"
).value;
if (event.keyCode === 13) {
this.setState({ ...this.state, isLoading: true });
axios.post(SEARCH_RESULTS_ENDPOINT, {
queryString: queryString,
}).then(response => {
this.setState({ ...this.state, searchResults: response.data });
this.setState({ ...this.state, isLoading: false });
});
}
};
render() {
if (this.state.isLoading) {
return <Spinner />
}
return (
<div>
<input
type="text"
placeholder="Search"
className="search-input"
onKeyDown={(e) => this.getSearchQuery(e)}
/>
<div>
{this.state.searchResults.map(result => (
<div key={result._id} >
<img src={result.picture} alt="avatar" />
<div >
<div>
<h2>{result.title}</h2>
<p>{result.date}</p>
<p>{result.postContent}</p>
<Link to={`/post/${result._id}`} className="read-more-btn">
<button className="read-more-btn">Read more</button>
</Link>
</div>
</div>
</div>
))}
</div>
</div>
);
}
}
export default Search;
and here is my CSS:
.search-input {
background: url(../../assets/images/search-icon.png) no-repeat scroll 12px 12px;
background-size: 20px 20px;
margin: auto;
display: block;
width: 500px;
margin-top: 30px;
height: 40px;
border: solid 1px #AAAAAA;
// text-indent: 30px;
}
.search-icon {
height: 20px;
width: 20px;
}
.no-results-found {
text-align: center;
margin-top: 50px;
}
.search-input::-webkit-input-placeholder { /* Chrome/Opera/Safari */
padding-left:35px;
}
input:focus{
outline: none;
}
::-moz-placeholder { /* Firefox 19+ */
padding-left:20px;
}
:-ms-input-placeholder { /* IE 10+ */
padding-left:20px;
}
:-moz-placeholder { /* Firefox 18- */
padding-left:20px;
}
Add some padding to search input as it will pad that much area.
.search-input {
background: url(../../assets/images/search-icon.png) no-repeat scroll 12px 12px;
background-size: 20px 20px;
margin: auto;
display: block;
width: 500px;
margin-top: 30px;
height: 40px;
border: solid 1px #AAAAAA;
padding-left: 20px;
}
Here's how I would do it.
Example with 2 variants
const { useState, useEffect } = React;
const App = () => {
return <div>
<div className="container">
<span className="icon">
<i className="fas fa-search"></i>
</span>
<input className="input" type="Text" placeholder="Search"/>
</div>
<div className="container1">
<span className="icon1">
<i className="fas fa-search"></i>
</span>
<input className="input1" type="Text" placeholder="Search"/>
</div>
</div>
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
.container {
border: 1px solid black;
width: 300px;
}
.input {
padding: .5rem .5rem .5rem 2rem;
border: none;
}
.input:focus {
outline: none;
}
.icon {
position: absolute;
margin-top: .5rem;
margin-left: .5rem;
}
.container1 {
margin-top: 1rem;
border: 1px solid black;
width: 300px;
display: flex;
}
.icon1 {
padding: .5rem 0 .5rem .5rem;
}
.input1 {
flex: 1 1 auto;
padding: .5rem;
border: none;
}
.input1:focus {
outline: none;
}
<script src="https://unpkg.com/react/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone#6/babel.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.12.0-2/js/all.min.js"></script>
<div id="root"></div>
I am carrying out a project in react for a web page. I am performing as a category bar where in the mobile version I should leave the screen but I want only the component to slide just and not the whole page.
Here is the design of how it should look
But currently it looks this way
Here is how it looks today
On the other hand, when you select something from this category, it is placed on the left side and removed from the category list, which would be that it is selected
Like this
The code of this component is here:
import './styles/Category.css'
import { cate } from '../assets/category_list.json'
class Category extends Component {
constructor(){
super();
this.state = {
cate,
selectItem: undefined,
opcion: 0
};
this.handaleSelect = this.handaleSelect.bind(this);
}
handaleSelect = (e,index) => {
this.setState({
selectItem: index,
opcion: 1
})
// console.log(index)
// console.log(this.state.selectItem)
}
handaleUnSelect = (e) => {
this.setState({
selectItem: undefined,
opcion: 0
})
}
selected() {
const select_pers = this.state.cate.filter(cate => {return cate.number === this.state.selectItem})
if (this.state.opcion === 1) {
return (<div className="box1 justify-content-center">
<div>
<img className="picture rounded-circle red-shadow" alt={select_pers.alt}src={require("../assets/img/"+select_pers[0].path_image)}></img>
</div>
<div className="text-box red-box" onClick={(e) => this.handaleUnSelect(e)}>
<p>{select_pers[0].title}</p>
</div>
</div> )
}
}
render(){
var catego = undefined;
var size = {
width: '808px',
};
if(this.state.opcion !== 0){
catego = this.state.cate.filter((cate) => {
return cate.number !== this.state.selectItem
});
size = {
width: '748px',
left: '31%',
};
}else{
catego = this.state.cate;
}
return (
<div className="d-flex justify-content-center ">
{ this.selected()}
<div className="Category" style={size}>
<div className="container boxe">
<div className="row">
{ catego.map(e =>
<div className="col" key={e.number}>
<div>
<img className="picture rounded-circle" alt={e.alt} src={require("../assets/img/"+e.path_image)}></img>
</div>
<div className="text-box" onClick={(x) => this.handaleSelect(x,e.number)}>
<p>{e.title}</p>
</div>
</div>
)}
</div>
</div>
</div>
</div>
);
}
}
export default Category;```
And the css file
.Category {
/* position: absolute; */
width: 858px;
height: 171px;
background: #ECF0F6;
border-radius: 200px;
/* left: 28%; */
margin-top: 5px;
}
.boxe {
/* background-color: green; */
text-align: center;
width: 95%;
height: 80%;
margin: 20px 20px 20px 20px;
/* padding-top: 18px; */
}
.box1{
/* position: absolute; */
/* background-color: yellow; */
/* left: 22%; */
margin-top: 28px;
text-align: center;
width: 130px;
float: left;
}
.justify-content-center{
padding-right: 10px;
}
.picture{
width: 80px;
height: 80px;
opacity: 0.8;
}
.text-box{
background: #FFFFFF;
height: 48px;
border: 1px solid #ECF0F6;
/* box-sizing: border-box; */
box-shadow: 0px 7px 64px rgba(0, 0, 0, 0.07);
border-radius: 800px;
margin-top: 6px;
/* width: 105px; */
cursor: pointer;
}
.text-box p{
font-family: Quicksand;
font-style: normal;
font-weight: 600;
font-size: 14px;
line-height: 20px;
letter-spacing: 0.25px;
color: #78869A;
padding-top: 12px;
cursor: pointer;
height: 48px;
}
.text-box p:hover{
color: #FF8B85;
}
.red-box{
background: #FF8B85;
}
.red-box p:hover{
color: gainsboro;
}
.red-box p{
color: white;
}
.red-shadow{
box-shadow: 0px 2px 10px #FF7575;
}
A working code snippet would be even more helpful.
Probably you could apply "display: flexbox" to the following element, instead of "float: left" to its children.
<div className="d-flex justify-content-center ">
Then you can play around with "flex-wrap" and "overflow" to achieve your design.