Change the position of button text and button icon - javascript

I have a button which contains an icon and some text. I want this button to be reusable. I also want to have the option of putting the icon left of the text or right of the text. I am passing in a prop (either "right" or "left") which will then adjust the CSS. Could anyone help me out with the CSS to swap the position of these two.
export const IconButton = ({icon,text, position}) => {
return (
<StyledIconButton className={position}>
<img src={icon} />
{text}
</StyledIconButton>
);
};
The StyledIconButton has a few default styles, but nothing important.
I am trying to use float but haven't been successful. Ideally I would avoid using flexbox as well.
.right {
img {
float: left;
margin-right: 0.375rem;
}
}
.left {
img {
float: right;
margin-left: 1.5rem;
}
}
As requested I am adding in the styles for StyledIconButton:
.styledbutton {
display: flex;
font-size: 1rem;
line-height: 2rem;
}
The above code won't actually swap the position.

I think using a flexbox and switching the direction would be the easiest way. You can learn about flexbox here.
But you can set left and right classes to decide on the direction of the flex.
.right {
flex-direction: row-reverse;
}
.left {
flex-direction: row;
}
And you would have set spacing and all other required styles on text and img separately, but this should do the trick

Related

CSS changing to display: flex affects how classes are applied to button

I have an unordered list whose li elements each contain a button as a child within them.
In Javascript I toggle adding a 'done' style (which crosses off the text) on the li elements when they are clicked like so
function crossOffList(e){
if (e.target.nodeName === "LI"){
e.target.classList.toggle("done");
}
}
The following is the li style:
li {
margin: 10px 0px;
**display: flex;**
justify-content: space-between;
align-items: center;
align-self: center;
}
The issue is that when I add display: flex; to the li style, the button also becomes crossed off after the li item is clicked, like so:
This is how the buttons are created and added to the li elements
function addDeleteButton(element){
element.innerHTML += " ";
var button = document.createElement("button");
button.appendChild(document.createTextNode("Delete"));
button.addEventListener("click", function(e) {
ul.removeChild(element);
})
//button.classList.add("delete-button");
element.appendChild(button);
}
// initialise all starting list elements
for (var i of document.getElementsByTagName("li")){
addDeleteButton(i);
i.addEventListener("click", crossOffList);
}
This behaviour of the button also becoming crossed off does not occur if the display is not set to flex. What is causing this behaviour and is there any way to use flex while having the button not be crossed off after click?
CSS text-decoration does not get inherited in the same way as other properties, it gets propagated. It cannot be overwritten (but it can be added to) by setting another value in a child element. There is some discussion of this at Override text-decoration on child element
However, MDN documentation states that this propagation does not apply when the child element is floating or absolute.
I have not been able to show any improvement using float but there is some improvement if we position the button absolutely when, and only when, the parent LI element has display:flex
For example putting class flex on each LI element if you want them flexed and removing it if not and changing the CSS to:
li {
position: relative;
margin: 10px 0px;
justify-content: space-between;
align-items: center;
align-self: center;
background-color:cyan;
}
li.flex {/* add .flex class to each li if you want them flexed*/
display: flex;
}
li.done {
text-decoration: line-through;
}
.flex button {
position:absolute;
right:0;
}
Some work will be needed to make sure the desired layout is achieved but at least this method does not strike out the button text in both flex and non-flex cases.

React JS buttons with css align on top of each other instead of horizontal align

I am working on React JS application. I have two buttons which I wanted to be equal width. Initially they are aligned next to each other. When I increased the button size with col-sm-8, the button were rendered on top of each other. When I hit F12, I checked that there was no margin or padding next to the button and confused why the buttons are forced to aligned on top of each other when the width is increased. There is enough room.
I tried a few styling changes like justify-items: center, aligned-items: center but so far nothing is working.
Here is my code
<div className={'my-toolbar'}>
<div className={''my-btn-group}
<button className={'col-sm-8'}>{'AAAAAAAA'}</button>
<button className={'col-sm-8'}>{'BBBBBBBB'}</button>
</div>
</div>
css
.my-toolbar {
display: flex;
aligned-items: center;
font-size; 16px
}
.my-btn-group {
display: inline-block
white-space: nowrap
}
You are mixing two type of display in CSS display: inline-block and display: flexconcentrate only on flex try this
.my-toolbar {
display: flex;
align-items: center;
font-size; 16px
}
.my-btn-group {
flex: 1
}
Try This:
.my-btn-group{
display:flex;
flex-direction:row;
flex-wrap:nowrap;
}
and remove display:inline-block and white-space...

Color changing theme through theme setting

I am doing one project in which I want to change the theme color, icons based on country. For example, Australia people well enter theme color: red so all icons, and text or background color should get a change to red.
Changing/Loading different CSS is one option but, I want the user will enter color and that color should be pass to CSS file and theme will change.
Is there any way through which I can pass user entered input color code in CSS file?
Using vanilla JavaScript and CSS variables :
function customThemeColors ()
{
// Get the flags container
const flags = document.querySelector( '.flags' );
// Reference
let current = null;
// Add a click event
flags.addEventListener( 'click', () => {
// Clicked target
let target = event.target;
// If target is not a button or if it is the last one clicked return
if ( target.nodeName !== 'BUTTON' || target === current ) return;
// Get the color from the button attribute
let color = target.getAttribute( 'data-theme-color' );
// Set the css variable on the whole document
document.documentElement.style.setProperty( '--custom-theme-color', color );
// Reference to the button clicked
current = target;
});
}
// Usage example
customThemeColors();
/* Using CSS variables */
.color
{
color: var( --custom-theme-color );
}
.background-color
{
background-color: var( --custom-theme-color );
}
/* Everything else is not important, it is only for demonstration */
body
{
display: flex;
flex-direction: column;
align-items: center;
}
.flags,
.container
{
display: flex;
justify-content: space-evenly;
align-items: center;
width: 400px;
height: 50px;
}
button
{
width: 75px;
height: 25px;
}
.container > div
{
height: 50px;
width: 200px;
display: flex;
justify-content: center;
align-items: center;
}
<div>CSS variables example</div>
<div class="flags">
<button data-theme-color="#00F">Brazil</button>
<button data-theme-color="#0F0">Australia</button>
<button data-theme-color="#F00">Canada</button>
</div>
<div class="container">
<div class="color">Color</div>
<div class="background-color">Background-color</div>
</div>
No, not unless you generate the CSS file server-side via a controller that accepts a query parameter with the color.
you can create a select tag, that have options with value for example:
<select>
<option value="red">Red</option>
<option value="blue">Blue</option>
</select>
https://www.youtube.com/watch?v=k2qJ8QbGbAU
heres a video that may help you see clearly what i mean.
It will be better to replace or add the css class or id of your page body so that you can easily change each and every style of that page. You can use jquery click function. For example: when you click on Australia just use addClass() function on body and add red class. You also need to write css for that red color theme as well.
Example:
$(document).ready(function(){
$('.australia').on('click', function(){
('body').addClass('red');
})
})
If necessary you can also remove previously added class while clicking another one. For more jquery function you can follow https://api.jquery.com.
You also need to add css for above red class like:
body.red {
background-color: #FF0000;
}
body.red p,
body.red span,
body.red h1,
body.red i {
background-color: #FF0000;
}
On that way you change color and theme of your whole page.

Solution/idea to call a function centrally and unified

So this one is a little bit hard to explain, but I'll try it:
I've got a div with the class .boxes. It's the wrapper of some looped elements. The looped elements have the class .box. Within this element, there are two more elements. One is the header with the class .box-header and one is the content with the class .box-content. I can open and close this elements with a click on it, so if it's closed, I just see the .box-header and if it's opened, I can see also the .box-content.
Here is a screenshot, which shows you how this looks like (grey container .boxes with the elements .box within in different states closed/opened):
I've got such lists in different components, so more than one time. The structure is mostly the same. So a example template of this looks like this:
<!--wrapper boxes-->
<div class="boxes">
<!--looped element box-->
<div class="box boxFor{{::box.id}}" ng-repeat="box in boxes" ng-class="{fill-container: boxes.length == 1}">
<!--box header-->
<div class="box-header" ng-click="box.open = !box.open">
{{::box.name}}
</div>
<!--box content-->
<div class="box-content" ng-if="box.open">
<!--some content-->
</div>
</div>
</div>
Here is a example style of the classes:
.boxes {
display: flex;
flex: 1 1 auto;
flex-direction: column;
overflow: auto;
.box {
display: flex;
flex-direction: column;
flex: 0 0 auto;
margin-bottom: 5px;
}
.box-header {
display: flex;
flex-direction: row;
justify-content: flex-start;
min-height: 30px;
line-height: 20px;
font-size: 16px;
padding: 5px;
}
.box-content{
padding: 5px;
display: flex;
flex-direction: column;
flex: 1 1 auto;
margin-top: 5px;
}
.fill-container {
flex: 1 1 auto;
}
}
Now one requirement was, that everytime I open a box, it should take 50% of the height from his wrapper. So .box should have a height: 50%; of .boxes. This mean, I can see in maximum 2 opened boxes in the same time. When I open a third etc., it also should take the same height of 50% from .boxes, but I have to scroll to see it. If I just have one box, it should fill the whole container when I open it, this is solved with the ng-class="{fill-container: boxes.length == 1}" and works fine. I also implemented a function, which calculates and sets the 50% correctly to .box and it's also correct on the screen like in my screenshots. I did it like this:
In the ng-click of my .box-header, I call a function named setBoxHeight() in my controller, which calculates and sets me the correct height:
<div class="box-header" ng-click="box.open = !box.open; ctrl.setBoxHeight()">
{{::box.name}}
</div>
The function looks like this:
setBoxHeight() {
if (this.boxes.length > 1) {
let boxHeaderHeight = $('.box-header').outerHeight();
let halfHeight = Math.round($('.boxes').outerHeight() / 2);
this.boxes.forEach((box: any) => {
let element = $('.boxFor' + box.id);
if (box.open) {
element.height(halfHeight);
} else {
element.height(boxHeaderHeight);
}
});
}
}
If you compare my template and this function, it should be clear what happens. This works also fine. The problem is, that I have to implement it in every list I have on different controllers for different components. I would like to unify it and run it centrally for all lists. It should be a clean solution. I tried to build a external component and pass throught the boxes and their class names with some bindings and call once the function, something like this:
<my-component items="ctrl.boxes" header-class="'.box-header'" wrapper-class="'.boxes'"></my-component>
I'm pretty sure, there is a cleaner way to do this. I hope my question is clear enough. Any ideas?
You could go for an all-CSS solution, and use the flex-basis property (see documentation). Basically, it sets the default size of flexible children; so you could set this to 50%, so each box would take half the height of boxes (since your flex-direction is set to column).
There is of course some implementation to do to adapt this to your project, but it's worth a shot (and perhaps cleaner?).
Here is a codepen to show you the trick.
EDIT: As #MrBuggy pointed out, this solution won't work in their case, since the boxes container doesn't have a fixed height.

Icon aligns to center when using display: table and margin: auto, but when I change to display:none it does not align to center after .show()

This works and properly aligns the icons:
#stepSuccess {
display: table;
margin: auto;
}
#stepFailure {
display: table;
margin: auto;
}
Here are the icons themselves:
<i class='icon-ok icon-4x icon-green'id='stepSuccess'></i>
<i class='icon-remove icon-4x icon-red'id='stepFailure'></i>
However, I need it to be at display: none for javascript purposes. When I try the code below it does not align to the center after I use .show() to display the icon.
#stepSuccess {
display: none;
margin: 0 auto;
}
#stepFailure {
display: none;
margin: 0 auto;
}
Here is the jQuery
if(checked)
{
//display correct-answer dialogue
$("#stepSuccess").show();
{
else
{
//display wrong-answer dialogue
$("#stepFailure").show();
}
How do I keep display: none and still have the code properly align to the center of the div (after .show() in javascript)? I also tried text-align: center;
Any help would be appreciated.
I suspect that when you use .show(), it sets display: inline instead of display: table. Instead of .show(), I would set up two classes, one for show and one for hide, and toggle the class.
Alternatively, you could also use:
if(checked)
{
//display correct-answer dialogue
$("#stepSuccess").css('display','table');
{
else
{
//display wrong-answer dialogue
$("#stepFailure").css('display','table');
}
instead of .show().
For the sake of less code, would you be able to just set the opacity to 0, rather than hide?

Categories