Extra pixels in CSS radiobutton group buttons that won't go away - javascript

EDIT: Changed the post name, which was incorrectly titled from another post !!
I have been building a sports app in React over the last several months, and I am struggling with a small cosmetic issue with my radio buttons. Immensely frustrating is the fact that despite my attempt at a reproducible example, the bug does not appear in my example below, although fortunately a variant of the issue is occurring. Here are my buttons:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
oneTwoFour: "1 Graph",
quarter: "All"
}
}
handleQuarterChange = (event) => {
this.setState({ quarter: event.target.value });
};
handleOneTwoFourChange = (event) => {
this.setState({ oneTwoFour: event.target.value });
};
render() {
const { oneTwoFour, quarter } = this.state;
const oneTwoFourOptions = ["1 Graph", "2 Graphs", "4 Graphs"];
const oneTwoFourButtons =
<form>
<div className="blg-buttons">
{oneTwoFourOptions.map((d, i) => {
return (
<label key={'onetwofour-' + i}>
<input
type={"radio"}
value={oneTwoFourOptions[i]}
checked={oneTwoFour === oneTwoFourOptions[i]}
onChange={this.handleOneTwoFourChange}
/>
<span>{oneTwoFourOptions[i]}</span>
</label>
)
})}
</div>
</form>;
const quarterOptions = ["All", "OT", "Half 1", "Half 2", "Q1", "Q2", "Q3", "Q4"];
const quarterButtons =
<form>
<div className="blg-buttons">
{quarterOptions.map((d, i) => {
return (
<label key={'quarter-' + i} style={{"width":"50%"}}>
<input
type={"radio"}
value={quarterOptions[i]}
checked={quarter === quarterOptions[i]}
onChange={this.handleQuarterChange}
/>
<span>{quarterOptions[i]}</span>
</label>
)
})}
</div>
</form>;
return(
<div>
<div style={{"width":"25%", "float":"left", "margin":"0 auto", "padding":"5px"}}>
{quarterButtons}
</div>
<div style={{"width":"25%", "float":"left", "margin":"0 auto", "padding":"5px"}}>
{oneTwoFourButtons}
</div>
</div>
);
}
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
.blg-buttons {
display: flex;
justify-content: center;
flex-wrap: wrap;
}
.blg-buttons input[type=radio] {
visibility:hidden;
width:0px;
height:0px;
overflow:hidden;
}
.blg-buttons input[type=radio] + span {
cursor: pointer;
display: inline-block;
vertical-align: top;
line-height: 1;
font-size: 1.0vw;
padding: 0.5vw;
border-radius: 0.35vw;
border: 0.15vw solid #333;
width: 90%;
text-align: center;
color: #333;
background: #EEE;
}
.blg-buttons input[type=radio]:not(:checked) + span {
cursor: pointer;
background-color: #EEE;
color: #333;
}
.blg-buttons input[type=radio]:not(:checked) + span:hover{
cursor: pointer;
background: #888;
}
.blg-buttons input[type=radio]:checked + span{
cursor: pointer;
background-color: #333;
color: #EEE;
}
.blg-buttons label {
line-height: 0;
font-size: calc(0.85vw);
margin-bottom: 0.1vw;
width: 90%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.2.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.2.0/umd/react-dom.development.js"></script>
<div id='root'>
Come On Work!
</div>
Also, here is a screenshot of an inspection of the buttons in my app (can be found at bigleaguegraphs.com/nba/shotcharts-pro as well), showing the true error that I am having:
The error is in this overhang of the buttons that is not due to padding or margin. I have seemingly gone through every single aspect of the CSS styling my radio buttons, and I have no idea why the element extends a few extra pixels outward to the right.
Amazingly / unfortunately, this is not occurring in the example above, although there is a different issue in the example above where the label element extends a few extra pixels upward (instead of rightward), that I cannot account for.
Any help with removing this extra couple of pixels on the button group would be very much appreciated!

.blg-buttons {
display: flex;
justify-content: center;
/* flex-wrap: wrap; you shouldn't need this */
flex-direction: column;
flex: 1;
}
.blg-buttons label {
display: flex;
line-height: 0;
font-size: 0.85vw;
flex: 1;
align-items: center;
margin-bottom: 5px; /* you don't need that 0.1vw */
font-weight: 700;
}
.blg-buttons input[type=radio]+span {
cursor: pointer;
display: flex;
flex: 1;
justify-content: center;
vertical-align: top;
line-height: 1;
font-size: 1vw;
padding: .5vw;
border-radius: .35vw;
border: .15vw solid #333;
width: 90%;
/* text-align: center; <-- you don't need this with flex */
color: #333;
background: #eee;
}
You should try and use flexbox where possible. I worked this out by playing with your site, so where i saw .nba_scp_cp_rbs i replaced with .blg-buttons (hope that's right). But yeh, avoid using stuff like width: 90%, with flex you rarely have to explicitly define widths, and you can size things based on padding & margins, leading to way less weird sizing bugs like yours :)
picture proof of it working

Related

Why does a bit of my background gradient show at certain screen sizes?

I can't figure out why I'm getting this little bit of green when the window is an odd number of pixels wide. I think it has something to do with sub-pixel rendering, but I'm just not sure where the green is coming from. It's just the 2nd div too which is weird.
I have some script that is animating the BG of this div. I'm sure this is part of the issue, but I can't figure out why it's only happening to my 2nd div.
I tried to manually set the width of this div, but I was hoping it would be responsive and scale with the window size.
let currentStage = 1
function performAction(selectedStage) {
currentStage = selectedStage
let stages = document.body.getElementsByClassName('stage-flow-item')
let stageLines = document.body.getElementsByClassName('stage-flow-line')
console.log("selectedStage: " + selectedStage)
for (let stage of stages) {
if (stage.id > currentStage) {
stage.classList.remove('completed')
stage.classList.add('active')
} else {
stage.classList.remove('active')
stage.classList.add('completed')
}
}
for (let stageLine of stageLines) {
if (stageLine.id > currentStage) {
stageLine.classList.remove('lineCompleted')
stageLine.classList.add('lineActive')
} else {
stageLine.classList.remove('lineActive')
stageLine.classList.add('lineCompleted')
}
}
}
.stage-flow-container {
display: flex;
justify-content: space-between;
align-items: center;
height: 70px;
padding: 0 30px;
}
.stage-flow-item {
width: 70px;
height: 70px;
min-width: 70px;
border-radius: 50%;
background-color: #ddd;
display: flex;
justify-content: center;
align-items: center;
font-size: 18px;
color: #fff;
cursor: pointer;
}
.stage-flow-item.active {
background-color: #ddd;
}
.stage-flow-item.completed {
background-color: #6ab04c;
}
.stage-flow-line {
width: calc(100vw);
height: 6px;
background-color: #ddd;
/* default color */
background: linear-gradient(to left, #ddd 50%, #6ab04c 50%) right;
position: relative;
background-size: 200%;
transition: .5s ease-out;
}
.stage-flow-line.lineCompleted {
background-position: left;
background-color: #6ab04c;
}
.stage-flow-line.lineActive {
background-position: right;
background-color: #ddd;
}
<div class="stage-flow-container">
<div id=1 class="stage-flow-item" onclick="performAction(1)">1</div>
<div id=1 class="stage-flow-line"></div>
<div id=2 class="stage-flow-item" onclick="performAction(2)">2</div>
<div id=2 class="stage-flow-line"></div>
<div id=3 class="stage-flow-item" onclick="performAction(3)">3</div>
</div>
I'm not sure if this is on the right track, but I'd eliminate the odd 100vw width on the connectors and instead make them flex. I'd then remove the 200% background size multiplier. By setting the gradient points to 100% the problem is gone. I really don't know if this covers your use case, though.
I converted from background gradient to a pseudo-element solution for the color transition. I think it's simpler. You'd probably have to use CSS animations (as opposed to simple transitions) to make it work otherwise. Of course, you could apply the same principle to the stage items as well, implementing a delay to crate a consistent animation across the item and the line.
Note that duplicated ID values are invalid in HTML. They must be unique. I've refactored to use data attributes instead and an event listener instead of inline JavaScript.
const stageEls = document.querySelectorAll('.stage-flow-item')
const lineEls = document.querySelectorAll('.stage-flow-line')
let currentStage = 1
stageEls.forEach(el => {
el.addEventListener('click', () => {
performAction(el.dataset.stage)
})
})
function performAction(selectedStage) {
currentStage = selectedStage
for (let el of stageEls) {
if (el.dataset.stage > currentStage) {
el.classList.remove('completed')
el.classList.add('active')
} else {
el.classList.remove('active')
el.classList.add('completed')
}
}
for (let el of lineEls) {
if (el.dataset.stage > currentStage) {
el.classList.remove('lineCompleted')
el.classList.add('lineActive')
} else {
el.classList.remove('lineActive')
el.classList.add('lineCompleted')
}
}
}
.stage-flow-container {
display: flex;
align-items: center;
height: 70px;
padding: 0 30px;
}
.stage-flow-item {
width: 70px;
height: 70px;
min-width: 70px;
border-radius: 50%;
background-color: #ddd;
display: flex;
justify-content: center;
align-items: center;
font-size: 18px;
color: #fff;
cursor: pointer;
}
.stage-flow-item.active {
background-color: #ddd;
}
.stage-flow-item.completed {
background-color: #6ab04c;
}
.stage-flow-line {
flex: 1;
height: 6px;
background: #ddd;
position: relative;
}
.stage-flow-line::after {
position: absolute;
content: '';
top: 0;
left: 0;
width: 0;
height: 100%;
background: #6ab04c;
transition: all 0.5s ease-out;
}
.stage-flow-line.lineCompleted::after {
width: 100%;
}
<div class="stage-flow-container">
<div data-stage=1 class="stage-flow-item">1</div>
<div data-stage=1 class="stage-flow-line"></div>
<div data-stage=2 class="stage-flow-item">2</div>
<div data-stage=2 class="stage-flow-line"></div>
<div data-stage=3 class="stage-flow-item">3</div>
</div>

Open an element under a button when the button is clicked in React

Having this design:
There is a table with some rows, when a row is clicked, that kebab button (3 vertical dots) is visible.
When the button is clicked it should open an element which has some data in it - in this case a list of actions.
The part with showing the kebab button is working:
{
id: 'my-button',
Cell: ({ cell }: CellProps<MyCell>) => {
if (cell.row.index === selectedIndex) {
return (
<div>
<Button
icon={<ThreeDots />}
onClick={toggleModal}
/>
</div>
);
}
return <div></div>;
}
}
when the row is clicked, the 3-dots button is visible. There is also a boolean which is initially set to false but toggles its value when the button is clicked (toggleModal).
But how can that element with the list added under the button?
Done something like:
{isModalOpened ? <div className='absolute'>test</div> : null}
Or maybe is there any online solution to fix this?
To fix this issue, you just need to use:
event.stopPropagation();
Please check my demo below:
function rowClick() {
const status = document.getElementById("actions").style.display;
document.getElementById("actions").style.display =
status === "block" ? "none" : "block";
}
function btnClick(event) {
event.stopPropagation();
const status = document.getElementById("nav").style.display;
document.getElementById("nav").style.display =
status === "block" ? "none" : "block";
}
.row {
width: 80%;
display: flex;
align-items: center;
justify-content: space-between;
background: lightblue;
padding: 10px 20px;
}
.actions {
display: none;
position: relative;
z-index: 1;
}
nav {
display: none;
position: absolute;
top: 50px;
right: 0;
z-index: 10;
background: white;
border: 1px solid grey;
min-width: 170px;
}
nav p {
padding: 10px;
border-bottom: 1px solid grey;
margin: 0;
}
nav p:last-child {
border-bottom: 0;
}
.btn {
display: inline-block;
width: 50px;
height: 50px;
line-height: 50px;
background: green;
text-align: center;
cursor: pointer;
color: white;
}
<div class='row' onclick="rowClick()">
This is a row <div class="actions" id="actions"><span class="btn" onclick="btnClick(event)">⁝</span><nav id="nav"><p>Do this</p><p>Do that</p></nav></div></div>

Darkmode with saas

I am trying to create darkmode with sass work flow, i found an explanation online but its in React and i currently dont know react, I understand the code to an extent but the whole changing of state seems confusing, how can i convert to vanilla Javascript, using this in a vanilla JS situation os my issue now
HTML
<main id="app-root">
<div class="theme-light">
<div class="app-container">
<h1 class="title">Light theme</h1>
<button class="button">A button</button>
</div>
</div>
<div class="theme-dark">
<div class="app-container">
<h1 class="title">Dark theme</h1>
<button class="button">A button</button>
</div>
</div>
</main>
CSS
/*
* Theme definitions
*/
$themes: (
light: (
backgroundColor: white,
textColor: #408bbd,
buttonTextColor: #408bbd,
buttonTextTransform: none,
buttonTextHoverColor: #61b0e7,
buttonColor: #fff,
buttonBorder: 2px solid #408bbd,
),
dark: (
backgroundColor: #222,
textColor: #ddd,
buttonTextColor: #aaa,
buttonTextTransform: uppercase,
buttonTextHoverColor: #ddd,
buttonColor: #333,
buttonBorder: 1px solid #333,
),
);
/*
* Implementation of themes
*/
#mixin themify($themes) {
#each $theme, $map in $themes {
.theme-#{$theme} & {
$theme-map: () !global;
#each $key, $submap in $map {
$value: map-get(map-get($themes, $theme), '#{$key}');
$theme-map: map-merge($theme-map, ($key: $value)) !global;
}
#content;
$theme-map: null !global;
}
}
}
#function themed($key) {
#return map-get($theme-map, $key);
}
/*
* Actual styles for the app
*/
body {
margin: 0;
}
html, body {
height: 100%;
}
#app-root {
margin: 0;
padding: 0;
height: 100%;
display: flex;
flex-direction: column;
> div {
display: flex;
flex: 1;
}
}
.app-container {
display: flex;
flex-direction: column;
flex: 1;
align-items: center;
justify-content: center;
.title {
font-family: sans-serif;
font-weight: lighter;
}
#include themify($themes) {
color: themed('textColor');
background-color: themed('backgroundColor');
}
.button {
max-width: 20em;
cursor: pointer;
border-radius: 5px;
padding: 15px 32px;
display: inline-block;
transition: color 0.1s, border-color 0.1s, background-color 0.1s;
#include themify($themes) {
border: themed('buttonBorder');
color: themed('buttonTextColor');
border-color: themed('buttonTextColor');
background-color: themed('buttonColor');
text-transform: themed('buttonTextTransform');
&:hover {
color: themed('buttonTextHoverColor');
border-color: themed('buttonTextHoverColor');
background-color: themed('buttonHoverColor');
}
}
}
}
How To Toggle Dark Mode
W3schools.com covers a nice example how to achieve this with vanilla JS
var element = document.body;
element.classList.toggle("dark-mode");
}
Just change classnames and you're good to go!

My react input type == number is not working

Edit2 - demo code is now working
Edit3 - it appears that the way I'm styling the radio group is breaking the functionality... I hide the input and display a span instead, and that appears to be a problem.
Although I've gained some experience over last several months working in react, something that's always been not 100% clear to me is best-practices with regards to inputs, specifically including radio buttons, checkboxes, and number inputs.
I currently have (what i think is) a bad approach to creating a simple 2-button radio-button-group:
const oneTwoFourOptions = ['2 Graphs', '4 Graphs'];
const oneTwoFourButtons =
(<form>
<div className='cbb-buttons cbb-buttons-clear' style={{ flexDirection: 'column' }}>
{oneTwoFourOptions.map((d, i) => {
return (
<label key={'onetwofour-' + i} style={{ margin: '0', height: '30px' }}>
<input
type={'radio'}
value={oneTwoFourOptions[i]}
disabled={loading}
checked={oneTwoFour === oneTwoFourOptions[i]}
onChange={this.handleOneTwoFourChange}
/>
<span style={{ width: '100%' }}>{oneTwoFourOptions[i]}</span>
</label>
);
})}
</div>
</form>);
...which is a form with a div with a label with an input + span, which seems like too much. Dont worry about the classNames / styles so much as the element structure.
However, this post is actually to address a number input that I am trying to make, and that is not currently working (either in my app, or in this stackoverflow post unfortunately). I am actively debugging this post, but see below for a general overview of my attempt at a number input.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
minMinutes: 0
}
}
handleMinMinutesChange = (event) => {
this.setState({ minMinutes: event.target.value });
}
render() {
let minMinutesInput =
(<label className='cbb-number-input'>
<input
type='number'
name='min-minutes-input'
value={this.state.minMinutes}
onChange={this.handleMinMinutesChange}
/>
<span>{this.state.minMinutes}</span>
</label>);
return(
<React.Fragment>
{minMinutesInput}
</React.Fragment>
)
}
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
label.cbb-number-input {
display: flex;
line-height: 0;
margin: 2px;
font-weight: 700;
}
input[type=number] {
width: 0;
height: 0;
visibility: hidden;
overflow: hidden;
}
span {
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
line-height: 1;
font-size: 14px;
width: 100px;
padding: 4px;
border-radius: 3px;
border: 1px solid #333333;
text-align: center;
}
<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'>
Come On Work!
</div>
In my app, clicking on the number input does nothing. It always shows zero, which is the initial app. I'm not sure why the change functionality is not working, and any help with this would be greatly appreciated. In particular, I would like to avoid using an excessive number of divs and form containers for this single number input, but ofcourse i need it working.
Thanks in advance for help with this!!
Edit - if you have a source to the very best, most up-to-date guide on react-best-practices for creating and styling radio buttons, checkboxes, and number inputs, (and also creating their handler functions), that would be even better than addressing my post. Thanks
The great thing about javascript is that you don't need to use an input if you want to increment or decrement a numbered value. The example below uses React's local state in combination with buttons. When a button is clicked, it's onClick handler will update state accordingly.
The first example allows you to click on the button to increment the value.
The second example allows you to increment or decrement the value based upon the clicked + or - button.
As far as CSS styling goes, you can find a great resource here and here.
As far as flex styling goes, you can find a great resource here.
However, in this day and age, most developers turn to a UI framework for quick development: ant-design, react-bootstrap, semantic-ui, and material-ui to name a few.
class App extends React.Component {
constructor(props) {
super(props);
this.state = { minutes: 0 };
this.handleIncreaseMinute = this.handleIncreaseMinute.bind(this);
this.handleDecreaseMinute = this.handleDecreaseMinute.bind(this);
}
handleIncreaseMinute() {
this.setState(state => ({ minutes: state.minutes + 1 }));
}
handleDecreaseMinute() {
this.setState(state => ({ minutes: state.minutes > 0 ? state.minutes - 1 : 0 }));
}
render() {
return(
<React.Fragment>
<div className="container">
<p className="label">Minutes:</p>
<button className="minutes" onClick={this.handleIncreaseMinute}>{this.state.minutes}</button>
</div>
<div className="container">
<p className="label">Minutes:</p>
<button className="decreaseButton" onClick={this.handleDecreaseMinute}>-</button>
<p className="minutesDisplay">{this.state.minutes}</p>
<button className="increaseButton" onClick={this.handleIncreaseMinute}>+</button>
</div>
</React.Fragment>
)
}
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
.container {
display: flex;
justify-content: flex-start;
align-items: center;
}
.label {
font-weight: 700;
margin-right: 10px;
}
.increaseButton, .decreaseButton {
cursor: pointer;
margin: 0 5px;
text-align: center;
font-size: 14px;
width: 50px;
padding: 4px;
border-radius: 3px;
border: 1px solid #333333;
transition: all 0.2s ease-in-out;
}
.decreaseButton {
background-color: #f56342;
color: white;
}
.decreaseButton:hover {
background-color: #be391c;
}
.increaseButton {
background-color: #1c2022;
color: white;
}
.increaseButton:hover {
background-color: #5a5a5a;
}
.minutesDisplay {
font-size: 14px;
width: 100px;
padding: 4px;
border-radius: 3px;
border: 1px solid #333333;
text-align: center;
}
.minutes {
cursor: pointer;
font-size: 14px;
width: 100px;
padding: 4px;
border-radius: 3px;
border: 1px solid #333333;
text-align: center;
transition: all 0.2s ease-in-out;
}
.minutes:hover {
background-color: #c1c1c1;
}
.minutes:focus, .increaseButton:focus, .decreaseButton:focus {
outline: 0;
}
<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>
Though, input type is number, it will be a type of String in JavaScript. So, it would be better to convert to type Number:
this.setState({ minMinutes: +event.target.value });
Also, set the initial value in your input field:
value={this.state.minMinutes || 0}
It is necessary for initial render.

Mouseover not working with velocity (no jQuery)

I can't seem to get the effect working. I know it can be done with css transitions/keyframes, but I'd prefer it in Velocity. Other effects work, and I'm using the same syntax, so I'm at a loss for what's causing this. I tried iterating through the document.getElementsByClass('menu') inside the function too, though Velocity does that by itself as far as I know. I also tried testing with just a single element id instead of a class. Nothing worked.
When I used the HTML onmouseover tag, however, I kept getting reference errors - function not defined.
var varb = document.getElementsByClassName('menu');
for (var i = 0; i < varb.length; i++) {
varb[i].addEventListener("mouseover", menuHover);
}
function menuHover() {
Velocity(varb, {
border: "1px",
border: "blue"
}, {
duration: 500
});
}
#navContainer {
display: flex;
justify-content: center;
width: 60%;
justify-content: space-around;
align-items: center;
}
.menu {
display: flex;
font-size: 0.77rem;
font-weight: bold;
width: 16%;
margin: 0 0.2rem 0 0.2rem;
height: 2rem;
justify-content: center;
line-height: 2rem;
font-family: Arial Black, sans-serif;
}
<nav id="navContainer">
<div class="menu">HOME</div>
<div class="menu">GALLERY</div>
<div class="menu">ACTIVITIES</div>
<div class="menu">ABOUT US</div>
<div class="menu">PRICE</div>
<div class="menu">CONTACT</div>
</nav>

Categories