I am trying to create a floating label where the label will go up when you click the input. But I am running into a problem where I am unable to call the class(the element) without affecting the other one. For example, I have 2 classes with the same class name, when I try to click on one of the other ones would work at the same time. I am trying to add an array, but as I have just started to learn JavaScript I do not know how to do it properly.
const floatinput = document.getElementsByClassName('entry-form-input');
const floatlabel = document.getElementsByClassName("tryingtoanimatelabel");
function forgetmove() {
floatlabel[0].classList.add("myanimatelabel");
}
function removemove() {
if (floatinput[0].value === "") {
floatlabel[0].classList.remove("myanimatelabel");
} else {
floatlabel[0].classList.add("myanimatelabel");
}
}
<div className="tryingtoanimate-container" onClick={forgetmove} onBlur={removemove}>
<input type="text" className="entry-form-input forgetinput" name="TeamName"/>
<label className="tryingtoanimatelabel">Team Name</label>
</div>
<div className="tryingtoanimate-container" onClick={forgetmove} onBlur={removemove}>
<input type="text" className="entry-form-input forgetinput" name="TeamName" onChange={this.handelChange}/>
<label className="tryingtoanimatelabel">Team Name</label>
</div>
you can try to pass an "event" as argument to the functions above.
In this case your event (click or blur, etc.) will be bind to the element on which event happened, and you can refer to element as event.target
function forgetmove(event){
event.target.classList.add("myanimatelabel");
}
function removemove(event){
if (event.target.previousSibling.value === "") { //take the previous node of the element on which event happened (event.target)
event.target.classList.remove("myanimatelabel");
} else {
event.target.classList.add("myanimatelabel");
}
}
Few edits to answer...
I suggest you to use "onBlur" event on "input,
also you can use event.CurrentTarget, if you want to take exact element on which event happened excluding their child elements.
The final code will looks like this:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleClick = this.handleClick.bind(this);
this.handleBlur = this.handleBlur.bind(this);
}
handleClick(event) { // using "currentTarget" to take element on which event happened, this will ignore cases where we would click on child elements
let _label = event.currentTarget.querySelector('label');
let _input = event.currentTarget.querySelector('input');
_label.classList.add('myanimatelabel');
_input.focus();
}
handleBlur(event) {
let _input = event.target; // using "target" to take element on which event happened
let _label = _input.nextElementSibling; // taking next element to input
if (_input.value === "") {
_label.classList.remove('myanimatelabel');
} else {
_label.classList.add('myanimatelabel');
}
}
render() {
return (
<div className="wrapper">
<div onClick={this.handleClick}>
<input type="text" onBlur={this.handleBlur} />
<label className="tryingtoanimatelabel">
Team Name
</label>
</div>
<div onClick={this.handleClick} >
<input type="text" onBlur={this.handleBlur}/>
<label className="tryingtoanimatelabel">Team Name</label>
</div>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
.wrapper > div {
position: relative;
margin-top: 30px;
margin-left: 30px;
display: inline-block;
}
.wrapper input {
padding-left: 10px;
padding-top:3px;
}
.wrapper label {
position:absolute;
left: 10px;
top: 3px;
transition-duration: 0.6s;
cursor: text;
}
.wrapper .myanimatelabel {
top: -25px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Add a numeric value at the end of the class name so it's unique or give each id="id1" and target that ID specifically that way
Related
I have the following function component. Within it, when a user clicks on any of the 4 divs, note_b, note_g, note_p, note_y, I want that class name to then be appended to the div with className note
This is my (incomplete) code
import React from 'react-dom';
import DraggableCore from 'react-draggable';
function Note(props) {
return (
<DraggableCore defaultPosition={{x: 1000, y: 200}}>
<div className={"note " + }>
<div id="note_head">
<div id="note_bin"></div>
<div className="note_b" onClick={}></div>
<div className="note_p" onClick={}></div>
<div className="note_g" onClick={}></div>
<div className="note_y" onClick={}></div>
<div id="note_exit"></div>
</div>
<p>
{props.message}
</p>
</div>
</DraggableCore>
)
}
export default Note;
Thank you #Andy, I took a second look at state hooks and came up with this:
import React, { useState } from 'react';
import DraggableCore from 'react-draggable';
function Note(props) {
const [bg, setBG] = useState('note_bg_b');
return (
<DraggableCore defaultPosition={{x: 1000, y: 200}}>
<div className={"note " + bg}>
<div id="note_head">
<div id="note_bin"></div>
<div className="note_b" onClick={() => setBG('note_b')}></div>
<div className="note_p" onClick={() => setBG('note_b')}></div>
<div className="note_g" onClick={() => setBG('note_b')}></div>
<div className="note_y" onClick={() => setBG('note_b')}></div>
<div id="note_exit"></div>
</div>
<p>
{props.message}
</p>
</div>
</DraggableCore>
)
}
export default Note;
This can probably be done in a cleaner, more efficient fashion. It is however functional.
You can use the onClick event handler for each of the four divs and add the className that was clicked on to the div with the className note.
EDIT for additional question: To prevent the added className from unloading when clicking within the note div, we can use an event listener to check where the click originated from and if it did not come from an element with the note_b, note_p, note_g, or note_y className, then the className should not be removed.
import React, { useState, useRef } from 'react';
import DraggableCore from 'react-draggable';
function Note(props) {
const [className, setClassName] = useState('');
const noteDiv = useRef(null);
const handleClick = e => {
setClassName(e.target.className);
}
//This function prevents the default event action from occurring when the page is unloaded.
//If the target element of the event does not have one of the specified class names, it removes the class from the element with the class "note".
const handleUnload = e => {
e.preventDefault();
const noteDiv = document.querySelector('.note');
if (!['.note', 'note_b', 'note_p', 'note_g', 'note_y'].includes(e.target.className)) {
noteDiv.classList.remove(e.target.className);
}
}
return (
<DraggableCore defaultPosition={{ x: 1000, y: 200 }}>
<div ref={noteDiv} className={`note ${className}`} onClick={handleUnload}>
<div id="note_head">
<div id="note_bin"></div>
<div className="note_b" onClick={handleClick}></div>
<div className="note_p" onClick={handleClick}></div>
<div className="note_g" onClick={handleClick}></div>
<div className="note_y" onClick={handleClick}></div>
<div id="note_exit"></div>
</div>
<p>
{props.message}
</p>
</div>
</DraggableCore>
)
}
export default Note;
If you separate out your classes a little, and add a data attribute for each note, you might get closer to what you need.
Instead of a className that looks like node_b use two classes note b - note can be the general class for all notes, and b can be the one that specifies one particular note. I've used colours here for clarity.
Adding the data attribute makes it more easy to identify each note in the code. In the click handler you can destructure that note id from the dataset of the clicked element, and then use it to set state, and you can use that state in the containing element.
Note: I've only used one click handler on the notes' containing element so that I can use event delegation.
const { useState } = React;
function Note({ message }) {
// Initialise a new state to hold the note id
const [ noteClass, setNoteClass ] = useState('');
// The handler first checks to see if the
// clicked element is a "note" element.
// if it is it destructures the note id from the
// element's dataset, and then uses it to set state
function handleClick(e) {
if (e.target.matches('.note')) {
const { note } = e.target.dataset;
setNoteClass(note);
}
}
// When the state changes the containing element's
// class changes too.
return (
<div className={noteClass}>
<div id="note_head">
<div id="note_bin" onClick={handleClick}>
<div data-note="b" className="note b">B</div>
<div data-note="p" className="note p">P</div>
<div data-note="g" className="note g">G</div>
<div data-note="y" className="note y">Y</div>
</div>
<p>{message}</p>
</div>
</div>
);
}
ReactDOM.render(
<Note message="Message" />,
document.getElementById('react')
);
.note { padding: 0.25em; border: 1px solid #4444; }
.note:not(:last-child) { margin-bottom: 0.25em; }
.note:hover { background-color: #fffff0; cursor: pointer; }
.b { color: red; }
.p { color: blue; }
.g { color: green; }
.y { color: gray; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>
I want to change css width property of my element on some condition
<div className="consoleLayoutRoot-sideMenu">
<ConsoleSideMenu />
</div>
css
.consoleLayoutRoot-sideMenu .ant-menu {
padding-top: 30px;
/* background-color: #191146 !important; */
}
I am doing this way..but nothing is happening
document.getElementsByClassName("conjnjnot-sideMenjnjbhbhu.annjn ").style.width = "77px";
That's not working because you're treating a list as though it were an element. But it's also fundamentally not how you would do this in a React project.
Instead, you'd have the component re-render when the condition becomes true (perhaps by setting a state member). When rendering the div, you optionally include a style or a class name depending on whether you want the width applied:
<div className={`consoleLayoutRoot-sideMenu ${shouldHaveWidthClass ? "width-class" : ""}`}>
<ConsoleSideMenu />
</div>
...where .width-class { width: 50px; } is in your stylesheet.
Or with inline style, but inline styles are best avoided:
<div className="consoleLayoutRoot-sideMenu" style={shouldHaveWidthSetting ? { width: "50px" } : undefined}>
<ConsoleSideMenu />
</div>
Here's an example (using a class);
const {useState} = React;
const ConsoleSideMenu = () => <span>x</span>;
const Example = () => {
const [includeWidth, setIncludeWidth] = useState(false);
const toggle = ({currentTarget: { checked }}) => {
setIncludeWidth(checked);
};
return <React.Fragment>
<div className={`consoleLayoutRoot-sideMenu ${includeWidth ? "width-class" : ""}`}>
<ConsoleSideMenu />
</div>
<label>
<input type="checkbox" onChange={toggle} checked={includeWidth} />
Include width class
</label>
</React.Fragment>;
};
ReactDOM.render(<Example />, document.getElementById("root"));
.width-class {
width: 50px;
}
.consoleLayoutRoot-sideMenu {
border: 1px solid blue;
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>
I need to list out a long name list inside my page while showing all names at first is not desirable.
So I try to add an expand more button on it.
However, using a button will keep the browser focus on that button after it's pressed, left the button position unchanged on the screen while the name was inserted before that button.
On the other hand, using any, not focusable element (eg. div with onclick function) will do the desired behavior but lost the accessibility at all. Making the "button" only clickable but not focusable.
How do I make the button flushed to list bottom like the snippet div block does? Or is there a better choice to expand the existing list?
const myArray = [
'Alex',
'Bob',
'Charlie',
'Dennis',
'Evan',
'Floron',
'Gorgious',
'Harris',
'Ivan',
'Jennis',
'Kurber',
'Lowrance',
]
const ExpandList = (props) => {
const [idx, setIdx] = React.useState(8)
const handleExpand = e => {
setIdx(idx + 1)
}
return <div className='demo'>
<h1>Name List</h1>
{myArray.slice(0,idx).map(
name => <p key={name}>{name}</p>
)}
<div>
<button onClick={handleExpand} children='Button Expand' className='pointer' />
<div onClick={handleExpand} className='pointer'>Div Expand</div>
</div>
</div>
}
ReactDOM.render(<ExpandList/>, document.getElementById('root'))
.demo>p {
display: block;
padding: 20px;
color: #666;
background: #3331;
}
.demo>div>div {
display: flex;
padding: 15px;
margin-left: auto;
color: #666;
background: #3331;
}
.pointer {
cursor: pointer;
}
.pointer:hover {
background-color: #6663;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.14.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.14.0/umd/react-dom.production.min.js"></script>
<div id='root' class='demo'>hello</div>
Removing focus from the button in the click handler is probably the most elegant approach: e.target.blur(). It will work on any HTML element, whether it is focusable or not (as with the div in your case).
const myArray = [
'Alex',
'Bob',
'Charlie',
'Dennis',
'Evan',
'Floron',
'Gorgious',
'Harris',
'Ivan',
'Jennis',
'Kurber',
'Lowrance',
]
const ExpandList = (props) => {
const [idx, setIdx] = React.useState(8)
const handleExpand = e => {
e.target.blur()
setIdx(idx + 1)
}
return <div className='demo'>
<h1>Name List</h1>
{myArray.slice(0,idx).map(
name => <p key={name}>{name}</p>
)}
<div>
<button onClick={handleExpand} children='Button Expand' className='pointer' />
<div onClick={handleExpand} className='pointer'>Div Expand</div>
</div>
</div>
}
ReactDOM.render(<ExpandList/>, document.getElementById('root'))
.demo>p {
display: block;
padding: 20px;
color: #666;
background: #3331;
}
.demo>div>div {
display: flex;
padding: 15px;
margin-left: auto;
color: #666;
background: #3331;
}
.pointer {
cursor: pointer;
}
.pointer:hover {
background-color: #6663;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.14.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.14.0/umd/react-dom.production.min.js"></script>
<div id='root' class='demo'>hello</div>
Inspired by #MiKo, temporally unmount the button after click and set a timeout to add it back seems to do the work. Since browser lose the focus on original expand button, this will keep content flush down without focusing the original button:
const ExpandList = (props) => {
const [idx, setIdx] = React.useState(8)
const [showBtn, setShowBtn] = React.useState(true)
const handleExpand = e => {
setShowBtn(false)
setIdx(idx + 1)
setTimeout(() => setShowBtn(true), 10)
}
return <div className='demo'>
<h1>Name List</h1>
{myArray.slice(0,idx).map(
name => <p key={name}>{name}</p>
)}
{showBtn?
<div>
<button onClick={handleExpand} children='Button Expand' className='pointer' />
<div onClick={handleExpand} className='pointer'>Div Expand</div>
</div> :
<div></div>
}
</div>
}
But I'm still looking a method that doesn't need to 'unmount' a thing which should be there all time.
good people.
i have a small program that adds table row elements, when "Add" is clicked. There is also an color change option when table cell is clicked. The problem is - when multiple elements are created, clicking on one of them, changes the color for all of them, though i have a onClick sitting only on the TD tags. How could this be fixed?
https://jsfiddle.net/mattighof/5nmcyL7b/
<table>
{this.state.list.map((element, index) => {
return (
<tr>
<td
className={this.state.textColor ? "trRed" : "trBlack"}onClick={this.handleClick}>
{element}
<div
onClick={e => this.removeElement(e, index)}
className="div"
/>
</td>
</tr>
);
})}
</table>
would highly appreciate your advice.
Since you're generating <td> elements in an anonymous function, using this inside it will refer to the parent closure, which, in this case, is the Table component. Therefore, the textColor property is local to the component itself, and not to the individual <td> elements.
You're already iterating through a list that you keep in the component state, so you can slightly change the element structure to allow you to keep arbitrary state data individually.
To do this, instead of adding a raw string to your list, add an object with the text and isSelected properties set to the desired values, and when rendering the <td> elements or changing colors, use the said properties. You can, of course, name these properties to your liking, and even add more properties to individually manage the states of your elements.
One other thing to note is that the current implementation of the handleClick function is unaware of the context that you're calling it from, so you'll also need to pass the index of the element when calling it, and update your state with a new list where the element at the specified index has its state updated.
Here's the revised functions as per my naming:
addElement() {
this.setState({
list: this.state.list.concat({
text: "element",
isSelected: false
})
});
}
handleClick(e, index) {
if (!this.state.list[index]) {
return;
}
// to avoid any side effects, we're taking the immutable data approach
// and creating a new list with intended values, rather than updating the list directly
const oldElement = this.state.list[index];
const newElement = Object.assign({}, oldElement, {isSelected: !oldElement.isSelected});
const newList = [].concat(this.state.list);
newList.splice(index, 1, newElement);
this.setState({
list: newList
});
}
...
{this.state.list.map((element, index) => {
return (
<tr>
<td
className={element.isSelected ? "trRed" : "trBlack"}
onClick={e => this.handleClick(e, index)}
>
{element.text}
<div
onClick={e => this.removeElement(e, index)}
className="div"
/>
</td>
</tr>
);
})}
...
I've also forked your fiddle and updated it with the code blocks I mentioned above: https://jsfiddle.net/w76frtgx/
There are many ways to handle this. Instead of relying on state to change the class, simply toggle the class trRed on the clicked target element.
To achieve this, modify handleClick to this:
handleClick(e) {
e.target.classList.toggle("trRed")
}
Edit the style rule trRed to this:
.trRed {
color: red;
}
And finally remove the textColor: true from state since it will no longer be used.
class Table extends React.Component {
constructor(props) {
super(props);
this.state = {
list: []
};
this.handleClick = this.handleClick.bind(this);
this.addElement = this.addElement.bind(this);
this.removeElement = this.removeElement.bind(this);
}
handleClick(e) {
e.target.classList.toggle("trRed")
}
addElement() {
this.setState({ list: this.state.list.concat("element") });
}
removeElement(e, index) {
e.stopPropagation();
this.setState({ list: this.state.list.filter((_, i) => index !== i) });
}
render() {
return (
<div className="container">
<button onClick={this.addElement} type="button">
Add
</button>
<table>
{this.state.list.map((element, index) => {
return (
<tr>
<td
onClick={this.handleClick}
>
{element}
<div
onClick={e => this.removeElement(e, index)}
className="div"
/>
</td>
</tr>
);
})}
</table>
</div>
);
}
}
ReactDOM.render(<Table />, document.getElementById("app"));
body {
padding: 20px;
}
td {
border: 1px solid black;
height: 15px;
padding: 5px;
}
tr {
border: 1px solid black;
position: relative;
}
table {
margin-top: 10px;
text-align: center;
width: 70px;
border: 1px solid black;
background-color: beige;
border-collapse: collapse;
}
.trRed {
color: red;
}
.div {
float: right;
width: 6px;
height: 6px;
background-color: red;
cursor: pointer;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>
applying a class to an element only when clicked
You could make 2 different click functions. One for trap and one for the rest.
For that you need to know which ones are the other ( safe ones ). See otherDivsIds in the below code. You find the other id's using the filter function in the idArray and then loop through them ( with forEach or something else ) and add event listeners to each of them.
I would also suggest to ' swap ' the naming of the variables trapBox and trapId. Vice versa would be better
See code below
var idArray = ['one','two','three','four'];
var trapBox = idArray[Math.floor(Math.random() * idArray.length)];
var trapId= document.getElementById(trapBox);
trapId.addEventListener('click', boomClickFunction, false);
var otherDivsIds = idArray.filter(id => id !== trapBox);
otherDivsIds.forEach(id => {
safeBox = document.getElementById(id);
safeBox.addEventListener('click', safeClickFunction, false)
})
var timeoutId = window.setTimeout(ticker, 5000);
function ticker() {
document.getElementById('timesUp').innerHTML = "Time's up!";
document.body.style.backgroundColor = "black";
}
function boomClickFunction() {
this.classList.add('boom')
}
function safeClickFunction() {
this.classList.add('safe')
}
div {
width: 20px;
height: 20px;
background-color: green;
margin: 20px;
float: left;
}
.boom {
background-color: red;
}
.safe {
background-color: lightblue;
}
#timesUp {
color: white;
}
<div id='one'>
</div>
<div id='two'>
</div>
<div id='three'>
</div>
<div id='four'>
</div>
<span id="timesUp">
</span>
You can add a class to an element by using classList.add('classToBeAdded').
In your case, you could put it in your clickFunction:
trapId.classList.add('boom');