A way to fake onclick events with only HTML and CSS styles. Perfect for avoiding JavaScript!
When I attempted to implement the hack as a way to navigate around a test website, I quickly discovered that when the inputs are toggled, they can only affect sibling elements. This meant that I couldn't just throw radio buttons into a nav bar and made it so pages would display when their corresponding inputs are toggled.
After some random experiments, I discovered that labels can be connected to inputs from anywhere in a website page! When you click on a label, it's good as clicking on the inputs!
So, I can just put input elements right next to what they're supposed to affect when toggled, and put the labels anywhere in the website such as a nav menu, and perfection! It's as if the website is running off JavaScript! Almost. This isn't a good replacement for JavaScript's onClick events, but if you're determined to avoid JavaScript at all cost, this will work for you.
- Since labels and inputs isn't grouped, inputs won't be able to affect labels. This makes it a bit harder for the user to see that their clicks was noticed or not.
- It's still abuse of CSS. CSS is meant for the front-end, not behavior stuff.
- You have to track your names more carefully. ID this, ID that, class there, etc. You do this less with JavaScript.
Implement a quick vanilla checkbox hack.
Success!
I discovered the scope issue when I tried to make the test below to look nicer by placing the inputs and labels into a div of their own to give the elements a different layout, this is shown in test two.
<div id="TestOne">
<input id="ItemOneR" type="radio" name="TestOne">
<label id="ItemOneL" for="ItemOneR">Label: Item One</label>
<input id="ItemTwoR" type="radio" name="TestOne">
<label id="ItemTwoL" for="ItemTwoR">Label: Item Two</label>
<input id="ItemThreeR" type="radio" name="TestOne">
<label id="ItemThreeL" for="ItemThreeR">Label: Item Three</label>
<input id="ItemFourR" type="radio" name="TestOne">
<label id="ItemFourL" for="ItemFourR">Label: Item Four</label>
<div id="ItemOneE">
<h1>Item One</h1>
</div>
<div id="ItemTwoE">
<h1>Item Two</h1>
</div>
<div id="ItemThreeE">
<h1>Item Three</h1>
</div>
<div id="ItemFourE">
<h1>Item Four</h1>
</div>
</div>
#TestOne{
display: flex;
flex-direction: column;
justify-content: space-around;
align-items: center;
border: 5px black solid;
}
#TestOne label{
border: 1px solid black;
margin: 10px auto 10px;
width: 130px;
padding: 10px;
}
#ItemOneR:checked ~ #ItemOneE{
background-color: red;
}
#ItemOneR:checked ~ #ItemOneL{
background-color: blue;
}
#ItemTwoR:checked ~ #ItemTwoE{
background-color: red;
}
#ItemTwoR:checked ~ #ItemTwoL{
background-color: blue;
}
#ItemThreeR:checked ~ #ItemThreeE{
background-color: red;
}
#ItemThreeR:checked ~ #ItemThreeL{
background-color: blue;
}
#ItemFourR:checked ~ #ItemFourE{
background-color: red;
}
#ItemFourR:checked ~ #ItemFourL{
background-color: blue;
}
Improve layout of the inputs and labels by putting them into their own container.
Failure! I didn't realize that I could only affect the Input's sibling elements using :checked in CSS.
<div id="TestTwoInputLayout">
<input id="TestTwoItemOneR" type="radio" name="TestTwo">
<label id="TestTwoItemOneL" for="TestTwoItemOneR">Label: Item One</label>
<input id="TestTwoItemTwoR" type="radio" name="TestTwo">
<label id="TestTwoItemTwoL" for="TestTwoItemTwoR">Label: Item Two</label>
<input id="TestTwoItemThreeR" type="radio" name="TestTwo">
<label id="TestTwoItemThreeL" for="TestTwoItemThreeR">Label: Item Three</label>
<input id="TestTwoItemFourR" type="radio" name="TestTwo">
<label id="TestTwoItemFourL" for="TestTwoItemFourR">Label: Item Four</label>
</div>
<div id="TestTwoItemOneE">
<h1>Item One</h1>
</div>
<div id="TestTwoItemTwoE">
<h1>Item Two</h1>
</div>
<div id="TestTwoItemThreeE">
<h1>Item Three</h1>
</div>
<div id="TestTwoItemFourE">
<h1>Item Four</h1>
</div>
#TestTwo{
display: flex;
flex-direction: column;
justify-content: space-around;
align-items: center;
border: 5px green solid;
}
#TestTwo label{
font-size: 2em;
margin: 10px auto 10px;
width: 130px;
padding: 10px;
transition: 2s;
}
#TestTwo label:hover{
background-color: green;
}
#TestTwoInputLayout{
padding: 20px;
}
#TestTwoItemOneR:checked ~ #TestTwoItemOneE{
background-color: red;
}
#TestTwoItemOneR:checked ~ #TestTwoItemOneL{
background-color: blue;
}
#TestTwoItemTwoR:checked ~ #TestTwoItemTwoE{
background-color: red;
}
#TestTwoItemTwoR:checked ~ #TestTwoItemTwoL{
background-color: blue;
}
#TestTwoItemThreeR:checked ~ #TestTwoItemThreeE{
background-color: red;
}
#TestTwoItemThreeR:checked ~ #TestTwoItemThreeL{
background-color: blue;
}
#TestTwoItemFourR:checked ~ #TestTwoItemFourE{
background-color: red;
}
#TestTwoItemFourR:checked ~ #TestTwoItemFourL{
background-color: blue;
}
Get this whole thingy working somehow!
Success! The placement of labels do not matter long as their for field properly links to inputs! It also turns out that you can connect more than one label to an input.
Due to to the label and the inputs not being siblings anymore, it's impossible for inputs to change the background of the labels when they're toggled. I haven't found an easy solution to that issue yet. This website page is using the result of this test. (The buttons for Test One, Two, and Three)
<div>
<label class="TestThreeItemOneL" for="TestThreeItemOneR">Label: Item One</label>
<label class="TestThreeItemTwoL" for="TestThreeItemTwoR">Label: Item Two</label>
<label class="TestThreeItemThreeL" for="TestThreeItemThreeR">Label: Item Three</label>
<label class="TestThreeItemFourL" for="TestThreeItemFourR">Label: Item Four</label>
</div>
<input id="TestThreeItemOneR" type="radio" name="TestThree">
<div id="TestThreeItemOneE">
<h1>Item One</h1>
</div>
<input id="TestThreeItemTwoR" type="radio" name="TestThree">
<div id="TestThreeItemTwoE">
<h1>Item Two</h1>
</div>
<input id="TestThreeItemThreeR" type="radio" name="TestThree">
<div id="TestThreeItemThreeE">
<h1>Item Three</h1>
</div>
<input id="TestThreeItemFourR" type="radio" name="TestThree">
<div id="TestThreeItemFourE">
<h1>Item Four</h1>
</div>
<div>
<label class="TestThreeItemOneL" for="TestThreeItemOneR">Label: Item One</label>
<label class="TestThreeItemTwoL" for="TestThreeItemTwoR">Label: Item Two</label>
<label class="TestThreeItemThreeL" for="TestThreeItemThreeR">Label: Item Three</label>
<label class="TestThreeItemFourL" for="TestThreeItemFourR">Label: Item Four</label>
</div>
#TestThree{
display: flex;
flex-direction: column;
justify-content: space-around;
align-items: center;
border: 5px green solid;
padding: 10px;
}
#TestThree div{
margin: 10px 0px 10px 0px;
}
#TestThree label{
font-size: 2em;
margin: 10px auto 10px;
width: 130px;
padding: 10px;
}
#TestThreeInputLayout{
padding: 20px;
}
#TestThreeItemOneR:checked ~ #TestThreeItemOneE{
background-color: red;
}
#TestThreeItemOneR:checked ~ .TestThreeItemOneL{
background-color: blue;
}
#TestThreeItemTwoR:checked ~ #TestThreeItemTwoE{
background-color: red;
}
#TestThreeItemTwoR:checked ~ .TestThreeItemTwoL{
background-color: blue;
}
#TestThreeItemThreeR:checked ~ #TestThreeItemThreeE{
background-color: red;
}
#TestThreeItemThreeR:checked ~ .TestThreeItemThreeL{
background-color: blue;
}
#TestThreeItemFourR:checked ~ #TestThreeItemFourE{
background-color: red;
}
#TestThreeItemFourR:checked ~ .TestThreeItemFourL{
background-color: blue;
}