Change parent block background when child input is checked(vanilla js) - javascript

I am new to the world of coding, and would be very grateful for an advise or any idea. I was wondering if it is possible to change color of current parent block background(.checkbox-container), when child input is checked. And the main problem is that I have multiple blocks with inputs, and require to change background color only to current block, not to all? As I understood there is no pure css solution without mark-up change, but this is not my case. Could someone please give any idea how that could be done in vanilla js?
Here is the link to fiddle:
https://jsfiddle.net/william_eduards/r4jxvuz5/4/
and visual code example here:
.checkbox-container {
display: flex;
align-items: center;
justify-content: center;
flex-wrap: nowrap;
background: #E8EBF0;
border: 1px solid #E8EBF0;
transition: background .3s ease-in-out;
margin-bottom: 8px;
border-radius: 4px;
}
<div class="dimensional-container checkbox-container">
<input type="checkbox" name="snapchat" class="checkbox-container__snapchat" id="snapchat">
<label for="snapchat">snapchat</label>
</div>
<div class="dimensional-container checkbox-container">
<input type="checkbox" name="facebook" class="checkbox-container__facebook" id="facebook">
<label for="facebook">Facebook</label>
</div>
<div class="dimensional-container checkbox-container">
<input type="checkbox" name="hangouts" class="checkbox-container__hangouts" id="hangouts">
<label for="hangouts">hangouts</label>
</div>

Find all input using querySelectorAll.
Loop over all input and addEventListener
check if the elment is checked or not using e.target.checked, If it is checked change its parent e.target.parentElement background style.
I've used red, you can select color on your own.
const allInputs = document.querySelectorAll("input");
allInputs.forEach(input => {
input.addEventListener("click", e => {
if (e.target.checked) {
e.target.parentElement.style.background = "#ff0000";
} else {
e.target.parentElement.style.background = "#E8EBF0";
}
})
})
.checkbox-container {
display: flex;
align-items: center;
justify-content: center;
flex-wrap: nowrap;
background: #E8EBF0;
border: 1px solid #E8EBF0;
transition: background .3s ease-in-out;
margin-bottom: 8px;
border-radius: 4px;
}
<div class="dimensional-container checkbox-container">
<input type="checkbox" name="snapchat" class="checkbox-container__snapchat" id="snapchat">
<label for="snapchat">snapchat</label>
</div>
<div class="dimensional-container checkbox-container">
<input type="checkbox" name="facebook" class="checkbox-container__facebook" id="facebook">
<label for="facebook">Facebook</label>
</div>
<div class="dimensional-container checkbox-container">
<input type="checkbox" name="hangouts" class="checkbox-container__hangouts" id="hangouts">
<label for="hangouts">hangouts</label>
</div>

Related

How to extend a div backwards?

Does anyone know how to extend a div 2 to the back, lowering the div 1 to the down, when clicking, in a simple way? It looks easy but with css it is not possible and with javascript it is difficult.
I want when clicking on the 2 extends back and the 1 goes down:
But instead this happens:
div 2 goes down.
Html and Css:
.frame {
width: 50%;
height: 400px;
font: bold 70px roboto;
color: black;
background-color: yellow;
float: left;
}
input:checked + .frame {
width: 100%;
}
input{
display: none;
}
<body class="gallery">
<input type="checkbox" id="a" />
<label for="a" class="frame a">1</label>
<input type="checkbox" id="b" />
<label for="b" class="frame b" style="background-color: green">2</label>
<input type="checkbox" id="c" />
<label for="c" class="frame a" style="background-color: green">3</label>
<input type="checkbox" id="d" />
<label for="d" class="frame b">4</label>
</body>
I tried with this javascript:
Demo
To Make the clicked div moves above all:
You can easily do that:
1-Make the gallery parent flex
2-Give order: 2; for all children
3-Change the order to 1 when checked
Apply:
body{
margin: 0;
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.frame {
width: 50%;
height: 100px;
font: bold 70px roboto;
color: black;
text-align: center;
order: 2;
}
input:checked + .frame {
width: 100%;
order: 1;
}
input{
display: none;
}
<body class="gallery">
<input type="checkbox" id="a"/>
<label for="a" class="frame" style="outline: 2px solid">1</label>
<input type="checkbox" id="b"/>
<label for="b" class="frame" style="outline: 2px solid">2</label>
<input type="checkbox" id="c"/>
<label for="c" class="frame" style="outline: 2px solid">3</label>
<input type="checkbox" id="d"/>
<label for="d" class="frame" style="outline: 2px solid">4</label>
<input type="checkbox" id="e"/>
<label for="e" class="frame" style="outline: 2px solid">5</label>
<input type="checkbox" id="f"/>
<label for="f" class="frame" style="outline: 2px solid">6</label>
</body>
And now, when you click anyone, it will go to the top and fill the parent width.
And when you click another one while the first is still active, it will be move to the top too and fill the parent width without affecting the older one.
If you want to stop having many frames selected at the top, just turn your inputs to be radio instead of checkbox and connect them all with the same name attribute.
To make the clicked div moves above its brother only:
Here you must dived every two frames in a separate row div and do the same job for the rest.
Apply:
body {
margin: 0;
display: flex;
flex-direction: column;
flex-wrap: nowrap;
}
.row {
width: 100%;
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.frame {
width: 50%;
height: 100px;
font: bold 70px roboto;
color: black;
text-align: center;
order: 2;
}
input:checked + .frame {
width: 100%;
order: 1;
}
input {
display: none;
}
<body class="gallery">
<div class="row">
<input type="checkbox" id="a" />
<label for="a" class="frame" style="outline: 2px solid">1</label>
<input type="checkbox" id="b" />
<label for="b" class="frame" style="outline: 2px solid">2</label>
</div>
<div class="row">
<input type="checkbox" id="c" />
<label for="c" class="frame" style="outline: 2px solid">3</label>
<input type="checkbox" id="d" />
<label for="d" class="frame" style="outline: 2px solid">4</label>
</div>
<div class="row">
<input type="checkbox" id="e" />
<label for="e" class="frame" style="outline: 2px solid">5</label>
<input type="checkbox" id="f" />
<label for="f" class="frame" style="outline: 2px solid">6</label>
</div>
</body>
Now if you clicked a div, it will go above its brother only, there is a small difference that the other elements will not merge with its brother.
And absolutely there is many many many other ways using css or javascript, but I think the first one does what you want.
Nice question. Yep, it does not have pure-css solution, unfortunately...
Fully agree with previous comment regarding to "swapping" strategy via "order" css property. It's definitely less code and more performant then "physically moving" html elements in DOM
Also, "display: grid" is always better then flex for multi-dimensional layouts (2-column in our case)
Supposing that initial requirement expects that any "even cell" should behave as "2", I see full solution like this:
const el_cells = document.querySelectorAll('.wrapper .cell');
el_cells.forEach((el_clicked, index) => {
const el_prev = index > 0 ? el_cells[index - 1] : null;
el_clicked.addEventListener('click', () => {
// checking if element was already expanded
const expanding = !el_clicked.classList.contains('active')
// restoring initial cells state
el_cells.forEach((el, i) => {
el.classList.remove('active');
el.style.order = i + 1;
});
if (expanding) {
// resizing selected cell
el_clicked.classList.add('active');
// swapping 'even' cell with previous one (f.e. 2 with 1, 4 with 3, etc...)
if (index % 2 === 1) {
el_clicked.style.order--;
el_prev.style.order++;
}
}
})
})
/* some not very important global styles */
* {
margin: 0;
padding: 0;
font-size: 40px;
font-weight: bold;
}
/* our yellow-green grid */
.wrapper {
display: grid;
grid-template-columns: 1fr 1fr;
}
.wrapper .cell {
height: 100px;
background: yellow;
}
.wrapper .cell.green {
background: green;
}
.wrapper .cell.active {
grid-column: span 2;
}
<div class="wrapper">
<div class="cell">1</div>
<div class="cell green">2</div>
<div class="cell green">3</div>
<div class="cell">4</div>
<div class="cell">5</div>
<div class="cell green">6</div>
<div class="cell green">7</div>
<div class="cell">8</div>
</div>
Improved this example a bit to make expanded cells to collapse on click :)
"Uladzimir Interesting, when you click on 2 it extends, then you click on 4 and everyone else resets. Can you tell me how to keep the 2 extended after clicking another one?"
Well, this small clarification adds additional "layer of complexity" to the initial question :)
The reason is that when initially you click "2" - you know that all "even elements" should push "odd ones" below them. On the other hand, when "2" gets expanded and you click "5" - how system should behave if not getting "2" collapsed ?
Seems that "5" has to push "4" below like "2" did that with "1".
So now logics of re-ordering elements has to become "generic" not like it was previously (re-ordering even-index elements when clicked)
And now user clicks, for example, "3", how to process this situation then ? Where do we have to push "1" in this case ? Under "5" probably ? I do not know... But, you see, lots of "edge cases" start appearing here...
What's bad here is that if you implement that - the solution would be definitely bulky, dirty and not very readable and understandable, unfortunately... So, I think, the best way here - to suggest your customers some easier-to-implement option (for example as I suggested initially when collapsing previously expanded elements)
Just a possible and not "too complicated" variant is to list selected elements always "on top":
const el_cells = document.querySelectorAll('.wrapper .cell');
let el_expanded = [];
el_cells.forEach((el_clicked) => {
el_clicked.addEventListener('click', () => {
const expanding = !el_clicked.classList.contains('active');
// resizing/collapsing current cell
if (expanding) {
el_clicked.classList.add('active');
} else {
el_clicked.classList.remove('active');
}
// refreshing expanded element collection
if (expanding) {
el_expanded = [el_clicked, ...el_expanded];
} else {
el_expanded = el_expanded.filter(_ => _ !== el_clicked);
}
// re-indexing not-expanded elements
el_cells.forEach((el, idx) => {
if (el_expanded.indexOf(el) !== -1) return;
el.style.order = 1000 + idx + 1 // 1000 added here to list them after "expanded" ones
})
// re-indexing expanded elements
el_expanded.forEach((el, idx) => {
el.style.order = idx + 1;
})
});
});
/* some not very important global styles */
* {
margin: 0;
padding: 0;
font-size: 40px;
font-weight: bold;
}
/* our yellow-green grid */
.wrapper {
display: grid;
grid-template-columns: 1fr 1fr;
}
.wrapper .cell {
height: 100px;
background: yellow;
}
.wrapper .cell.green {
background: green;
}
.wrapper .cell.active {
grid-column: span 2;
}
<div class="wrapper">
<div class="cell">1</div>
<div class="cell green">2</div>
<div class="cell green">3</div>
<div class="cell">4</div>
<div class="cell">5</div>
<div class="cell green">6</div>
<div class="cell green">7</div>
<div class="cell">8</div>
</div>
Again, this is not "ideal" option, you see. With lots of elements in list it would also require some "scroll-to-top" logics :( Anyway, it is understandable, predictable and requires "not too much" code to create and maintain

Multi-line label for radio button

I would like "This is label" text to stack right underneath the "Prefix Text", while radio button to be aligned right next "Prefix Text". Is it possible to get this done by updating class for div element that contains "This is label" only? I would like to keep label-container class untouched if possible as I may add icon in front of "Prefix Text" so I will need display: inline-flex to wrap them up. https://codepen.io/Judoboy/pen/OJQqPEW?editors=1100
.label-container {
display: inline-flex;
justify-content: flex-start;
align-items: center;
}
.label-text {
display: flex;
justify-items: center;
align-items: center;
height: 100%;
}
.prefix {
font-weight: bold;
}
.text-spacing {
padding-inline-start: 8px;
padding-inline-end: 4px;
}
<label class="label-container">
<input type="radio" />
<div class="label-text text-spacing prefix">Prefix Text</div>
<div class="label-text text-spacing">This is label</div>
</label>
You can do this:
Wrap both Prefix Text and This is label with additional div.
Change align-items in .label-container class to start (you can keep display: inline-flex).
.label-container {
display: inline-flex;
justify-content: flex-start;
align-items: start;
}
.label-text {
display: flex;
justify-items: center;
align-items: center;
height: 100%;
}
.prefix {
font-weight: bold;
}
.text-spacing {
padding-inline-start: 8px;
padding-inline-end: 4px;
}
<label class="label-container">
<input type="radio" />
<div>
<div class="label-text text-spacing prefix">Prefix Text</div>
<div class="label-text text-spacing">This is label</div>
</div>
</label>
just use some transform.
.AlignmentFix {
transform: translate(-100%, 17px);
}
<label class="label-container">
<input type="radio" />
<div class="label-text text-spacing prefix">Prefix Text</div>
<div class="label-text text-spacing AlignmentFix">This is label</div>
</label>
// finding the only <button> element in the document, and binding
// an anonymous Arrow function as the event-handler for the 'click'
// event on that <button>:
document.querySelector('button').addEventListener('click', (e) => {
// we retrieve the first element in the document that matches
// the selector supplied to document.querySelector():
let original = document.querySelector('label.label-container'),
// we then clone that node, and its descendant elements
// with the Boolean true argument passed to Node.cloneNode():
clone = original.cloneNode(true);
// e.currentTarget is the element to which the anonymous function
// was bound; from that element we navigate to the first ancestor
// element that matches the selector and then append the 'clone'
// to that <main> element:
e.currentTarget.closest('main').append(clone);
});
/* overriding browser default layout calculations,
and zeroing all margin and padding for cross-
browser consistency: */
*,
::before,
::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
/* element added for layout purposes, to avoid changing the <body>
element's styles in case of conflict with your real-world
preferences: */
main {
border: 1px solid #000;
border-radius: 1em;
display: flex;
flex-flow: row wrap;
gap: 0.5em;
justify-content: space-between;
margin-block: 1em;
margin-inline: auto;
padding: 0.5em;
width: clamp(10rem, 60vw, 1000px);
}
/* styling the <button> to occupy the whole width, or a full
'row': */
button:first-child {
flex-basis: 100%;
flex-grow: 1;
padding-block: 0.5em;
}
.label-container {
/* while 'inline grid' (note the space) is a valid property-
value, regardless of Chrome's claim to the contrary,
I've changed the property-value to 'inline-grid' for
compatibility with Chrome, Edge, etc: */
display: inline-grid;
/* here we create a grid-layout of two columns, and two
rows with three 'cells'. The first cell is placed in
the first column, and spans both rows. The other
cells take only one 'cell' each. */
grid-template-areas: "radio prefix" "radio text";
}
.label-text {
display: flex;
justify-items: center;
align-items: center;
height: 100%;
}
.prefix {
font-weight: bold;
}
input {
align-self: center;
}
.text-spacing {
grid-column: 2;
padding-inline-start: 8px;
padding-inline-end: 4px;
}
<!-- this element is purely for the wrapping, to supply padding and layout; obviously
adjust to your preferences: -->
<main>
<!-- added a <button> to handle the addition of new <label> elements to demonstrate the layout -->
<button>Add another <label> element</button>
<label class="label-container">
<input type="radio">
<div class="label-text text-spacing prefix">Prefix Text</div>
<div class="label-text text-spacing">This is label</div>
</label>
<label class="label-container">
<input type="radio">
<div class="label-text text-spacing prefix">Prefix Text</div>
<div class="label-text text-spacing">This is label</div>
</label>
<label class="label-container">
<input type="radio">
<div class="label-text text-spacing prefix">Prefix Text</div>
<div class="label-text text-spacing">This is label</div>
</label>
</main>
JS Fiddle demo.

Hide certain inputs and show others using a switch button in jQuery

Hello Stackoverflow community, hope that you're doing well, what I'm trying to do is when I click the switch button I want it to hide certain inputs and show others, my code is a form to add students and teachers, since there are cummon inputs I tought about hide the uncummon one when I press the switch button and when I click it again do the opposite but all of that seem to be failed, I can only hide some and when I click it again it won't work, here what I did:
The Jquery code:
$(document).ready(function(){
$('.teacher').hide();
$('.switch').click(function(){
$('.student').hide();
$('.teacher').show();
});
});
The HTML code:
<label>Student </label>
<label class="switch">
<input type="checkbox" id="switchVal" value="0">
<span class="slider"></span>
</label>
<label> Teacher</label>
$('.teacher').hide();
$('.switch').click(function() {
$('.student').toggle();
$('.teacher').toggle();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class='student'>Student</div>
<div class="switch">
<input type="checkbox" id="switchVal" value="0">
<span class="slider"></span>
</div>
<div class='teacher'>Teacher</div>
Use $(".teacher, .student").toggle();
Or, if needed, for more granular control you could always get the current checkbox state using
const isChecked = this.checked; // boolean`
Example:
jQuery($ => {
$(".teacher").hide();
$("#switchVal").on("input", function() {
$(".teacher, .student").toggle();
});
});
.toggler {
display: inline-flex;
gap: 0 5px;
cursor: pointer;
}
.toggler-checkbox {
display: inline-block;
width: 35px;
height: 15px;
background: #444;
border-radius: 1.2em;
}
.toggler-checkbox::before {
content: "";
position: relative;
display: inline-block;
width: 15px;
height: 15px;
background: #0bf;
border-radius: 1em;
transition: transform 0.3s;
}
.toggler input:checked ~ .toggler-checkbox::before {
transform: translateX(20px);
}
.toggler-label {
user-select: none;
}
.toggler-label:nth-of-type(1) {
order: -1;
color: #0bf;
}
.toggler input:checked ~ .toggler-label:nth-of-type(1) {
color: inherit;
}
.toggler input:checked ~ .toggler-label:nth-of-type(2) {
color: #0bf;
}
<label class="toggler">
<input type="checkbox" id="switchVal" value="0" hidden>
<span class="toggler-checkbox"></span>
<b class="toggler-label">Student</b>
<b class="toggler-label">Teacher</b>
</label>
<ul>
<li class="student">Student: Anna</li>
<li class="student">Student: John</li>
<li class="teacher">Teacher: Mark</li>
<li class="student">Student: Tara</li>
<li class="teacher">Teacher: Zack</li>
</ul>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>

hiding a "No file chosen" tooltip in Javascript

I know there are many question about it, but they don't answer properly.
After readings and looking for, I tried this:
<input id="ext-element-47" class="x-input-file x-input-el" type="file" accept="" style="display:none">
hiding the file-input and then
this.element.down(".x-input-file").dom.click();
this works on Chrome's console but in my JS code it doesn't. It doesn't click.
Anyone knows why? and what can I do for make click?
Notes:
I need to make click because the file element is not visible and so when it clicks it does not show unless I do element.click ().
Here is an example what I'm doing:
document.getElementsByClassName('o-file-field-input')[0].click()
.o-file-field-input {
display: none;
}
.o-big-btn {
background-color: red;
height: 3em;
width: 3em;
}
<div class="x-container x-unsized o-cont-option" data-componentid="ext-container-5" id="ext-container-5">
<div class="x-inner x-align-center x-pack-center x-horizontal x-layout-box" id="ext-element-50">
<div class="x-button x-button-plain open-field-icon o-big-btn x-layout-box-item x-flexed x-stretched" id="ext-OUI_BaseButton-1" data-componentid="ext-OUI_BaseButton-1" tabindex="0" style="-webkit-box-flex: 1;">
<span class="x-button-icon x-shown smf smf-upload-file" id="ext-element-45"></span>
<div class="o-button-bg"></div>
<div class="x-unsized x-field-input x-has-height" id="ext-fileinput-1" data-componentid="ext-fileinput-1" style="height: 38px;">
<input id="ext-element-47" class="x-input-file x-input-el o-file-field-input" type="file" accept="">
<div class="x-field-mask x-hidden-display" id="ext-element-48"></div>
<div class="x-clear-icon" id="ext-element-49">
</div>
</div>
</div>
</div>
</div>
See ya!
Here's what I usually do: Wrap the input inside a <label> element, and then style the element as a button, for example:
.pretty-file {
border: 1px solid #000;
cursor: pointer;
display: inline-block;
padding: 5px 15px;
}
.pretty-file input[type="file"] {
display: none;
}
<label class="pretty-file">
Choose File
<input type="file" />
</label>
This finally works well:
var obElement = document.getElementsByClassName('input-file')[0];
//the title property overrides tooltip's description
obElement.setAttribute('title', ' ');
.flex-style{
display: flex;
}
.input-file{
opacity: 0;
margin-left: -40px;
width: 40px;
height: 45px;
}
.icon{
width: 40px;
height: 45px;
background-color: blueviolet;
}
<div class='flex-style'>
<div class='icon'></div>
<input class='input-file' type='file'>
</div>

Checkbox checked CSS

Hi everyone i am trying to make a checkbox using CSS.
I have created this DEMO from codepen.io
In this demo you can see there is a tree checkbox button (YES,NO,MAYBE)
My question is this: Let's say the user first clicked the button YES.The user then gave up and click on the NO button. I want to make it when user clicked NO button then Yes button automatically unchecked. How can i do that anyone can help me ?
HTML
<div class="container">
<input type="checkbox" class="yesno"/>Yes
<input type="checkbox" class="yesno"/>no
<input type="checkbox" class="yesno"/>maybe
</div>
CSS
.container {
margin: 50px;
}
.yesno {
-webkit-appearance: none;
-moz-appearance: none;
width: 30px;
height: 20px;
border-radius: 14px;
outline: 0;
background: #9da6b0;
-webkit-transition: all 0.15s ease-in-out;
cursor: pointer;
}
.yesno:after {
content: "";
display: block;
width: 14px;
height: 14px;
background: #fff;
border-radius: 11px;
position: relative;
top: 3px;
left: 3px;
-webkit-transition: all 0.15s ease-in-out;
}
.yesno:checked {
background: #529ecc;
}
.yesno:checked:after {
left: 13px;
}
You could simply make those elements radio buttons with the same name attribute:
Updated Example
In doing so, the elements are mutually exclusive, and only one can be selected at once.
<div class="container">
<input type="radio" name="yes-no" class="yesno"/>Yes
<input type="radio" name="yes-no" class="yesno"/>no
<input type="checkbox" class="yesno"/>maybe
</div>
They should definitely be radio buttons, but here's the JavaScript.
<div class="container">
<input type="checkbox" class="yesno" id="yesCheckbox"/>Yes
<input type="checkbox" class="yesno" id="noCheckbox"/>no
<input type="checkbox" class="yesno" id="maybeCheckbox"/>maybe
</div>
$('#yesCheckbox').click(function() {
if($(this.prop('checked')))
{
$('#noCheckbox').prop('checked', false);
}
});

Categories

Resources