The simplest way to disable <a> links is by custom CSS-class "disabled" which sets the pointer-events to none.
How ever title-Attributes where we would like to display the reason why the link is disabled as hover text wont work anymore...
See example below:
a.disabled,
a.disabled:visited {
pointer-events: none;
color: grey;
}
active Link with hover text
<br>
disabled link with hidden hover text
I see two possibility which are both not really elegant:
Wrapping the <a> and add title to parent. But this is an unnecessary wrapping element and may affect styling.
a.disabled,
a.disabled:visited {
pointer-events: none;
color: grey;
}
active Link
<br>
<span title="link is disabled because of...">
disabled link
</span>
Since we're using deactivated anchor tags mostly styled as a button, we could use <button> instead. But unfortunately onclick navigation on buttons can not be opened optionally in a separate windows like <a> do (via context menu or click on the middle mouse button). This could be added with additional Javascript but this would be a even worse solution...
a.disabled,
a.disabled:visited {
pointer-events: none;
color: grey;
}
<button href="#" title="link is clickable" onclick="location.href='http://www.google.com'">
active button
</button>
<br>
<button title="link is disabled because of..." onclick="location.href='http://www.google.com'" disabled>
disabled button
</button>
I even don't understand why there is no disabled-attribute for <a> in the html standard like buttons have...
Any ideas for a really proper solution to disabled anchor tags but still displaying title hover texts? It would be nice to find an CSS only solution or a similar approach.
You can use the title attribute with disabled links,
you have to modify your code this way:
a.disabled {
pointer-events: auto;
color: grey;
}
a.disabled:active {
pointer-events: none;
}
This is an exemple using the attribute title and a custom attribute named data-tooltip to show the tooltip text:
a.disabled {
pointer-events: auto;
color: grey;
}
a.disabled:active {
pointer-events: none;
}
/* Tooltip **/
[data-tooltip] {
position: relative;
z-index: 10;
}
[data-tooltip]::after {
pointer-events: auto;
background: #444;
border-radius: 0.25rem;
box-shadow: 0 1rem 2rem -0.5rem rgba(0, 0, 0, 0.25);
color: #fff;
content: attr(data-tooltip);
display: inline-block;
font-size: 0.75rem;
letter-spacing: 1px;
line-height: 1;
max-width: 11rem;
opacity: 0.8;
padding: 0.375rem 0.25rem;
position: absolute;
left: 50%;
bottom: calc(100% + 0.25rem);
text-align: center;
transform: translate(-10%, 0.25rem);
user-select: none;
-webkit-user-select: none;
vertical-align: middle;
visibility: hidden;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
z-index: 999;
}
[data-tooltip]:hover::after {
visibility: visible;
opacity: 0.8;
transform: translate(-50%, 0.125rem);
z-index: 9999 !important;
transition: opacity 200ms ease-in-out, transform 500ms ease-in-out;
}
<br>
<br>
active Link with hover text
<br>
disabled link with hidden hover text
If you're fine with using a little bit of Javascript, you could simply go like this:
document.addEventListener('click', function(event) {
if (event.target.matches('a.disabled'))
event.preventDefault();
})
a.disabled,
a.disabled:visited {
color: grey;
}
active Link with hover text
<br>
disabled link with hidden hover text
Related
I am trying to add a popup when hover on my text (span).
I'm trying to do it like they explain in W3schools:
https://www.w3schools.com/css/css_tooltip.asp
But I am already using style in my span to color the text that I want to hover, so if I add the class with the properties from w3schools to my span the text is gonna be hidden since they have visibility: hidden; in the span class.
I am very new to this, so I would be glad if someone could help me.
If you want to add color to the span text (My text) then add a color property to .tooltip class in the w3schools example
However if your goal is adding color to tooltip text then adjust the color property in .tooltip .tooltiptext{}
This is the same example from w3schools
.tooltip {
position: relative;
display: inline-block;
border-bottom: 1px dotted black;
/* color of the span text */
color: rgb(119, 162, 241);
}
.tooltip .tooltiptext {
visibility: hidden;
width: 120px;
background-color: black;
/* color of the tooltip text */
color: #fff;
text-align: center;
border-radius: 6px;
padding: 5px 0;
position: absolute;
z-index: 1;
}
.tooltip:hover .tooltiptext {
visibility: visible;
}
<div class="tooltip">Hover over me
<span class="tooltiptext">My text</span>
</div>
I'm also very new to HTML and CSS, but I'll try to answer, anyway.
You can try to nest spans...
Taking W3Schools' code as an example, it will look something like this:
.p {
text-align: left;
}
.firstSpan {
color: rgb(119, 162, 241)
}
.firstSpan .secondSpan {
visibility: hidden;
width: 120px;
background-color: gray;
color: #fff;
text-align: center;
border-radius: 6px;
padding: 5px 0;
position: absolute;
z-index: 1;
}
.firstSpan:hover .secondSpan {
visibility: visible;
}
<p>Let's try some 'nested' spans.</p>
<p>This is some text in a paragraph. You can hover <span class="firstSpan">over me<span class="secondSpan">Some text.</span></span> and you'll see some text.
</p>
<p>Another text in another paragraph. Hover <span class="firstSpan">over me<span class="secondSpan">A hint.</span></span> and you'll see some text — Maybe a hint.
</p>
Depending on what you want, nested spans may not be the best practice, but if what you're looking for is a simple inline container... go for it.
I hope I was helpful. 😉
I've got a ::after element on label for a checkbox input.
Everything works on live-server in an editor, element is visible, :checked animation works, everything is ok.
I pushed the code on a repository on github and when I open the page, ::after element isn't showing up.
Here's HTML:
<li class="main__list-item">
<div class="main__list-item-container">
<input type="checkbox" id="question-1" class="main__list-question"></input>
<label for="question-1" class="main__label">How many team members can I invite?</label>
<p class="main__list-answer">You can invite up to 2 additional users on the Free plan.
There is no limit on team members for the Premium plan</p>
</div>
</li>
Here's CSS:
.main__label {
cursor: pointer;
font-size: 1.4rem;
color: var(--color-text-secondary);
transition: all 0.4s;
width: 100%; }
.main__label::after {
content: url("/img/icon-arrow-down.svg");
display: inline-block;
position: absolute;
right: 0;
transition: all 0.4s; }
.main__label:hover {
color: var(--color-text-main); }
.main__list-question {
appearance: none;
position: absolute;
right: 0;
cursor: pointer;
outline: none; }
.main__list-question:checked ~ .main__label::after {
transform: rotate(180deg); }
.main__list-question:checked ~ .main__list-answer {
display: block;
margin-top: 0.5em;
color: var(--color-text-tertiary);
animation: fadeIn 0.3s ease-in-out; }
.main__list-question:checked + .main__label {
font-weight: 700;
font-size: 1.4rem; }
even without visible ::after element, all animations work still, font-size changing, p element showing and etc.
This is from vs code live-server
This is from github page
the Problem ist the image Path:
you have to change the URL:
content: url(../img/icon-arrow-down.svg);
I have a problem with my custom checkbox.
If you click on a checkbox element, move the mouse cursor and then release the click inside the checkbox area, the checkbox is checked.
However, if you do the same on a custom checkbox (here, a div inside a label), the checkbox isn't checked.
It's a problem because if you want to quickly check a checkbox, you may move the mouse after pressing the button of the mouse and before releasing it, thus not toggling the checkbox.
The user is obligated to click without moving the mouse.
I know I can use JS to emulate a checkbox with a div, but I want the HTML to be semantically correct, so: Is it possible to fix it without js?
Here's the code :
/* 1. Hide the checkbox */
.hidden {
/* https://zellwk.com/blog/hide-content-accessibly/ */
border: 0;
clip: rect(0, 0, 0, 0);
height: auto;
margin: 0;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
white-space: nowrap;
}
/* 2. Use a label to retrieve the click event */
label {
/* not used directly to prevent the bug in firefox https://bugzilla.mozilla.org/show_bug.cgi?id=608180 */
pointer-events: none;
display: inline-flex;
}
label > input {
/*usefull for testing only*/
pointer-events : all;
}
label > .customCheckbox {
cursor: pointer;
display: inline-flex;
justify-content: center;
align-items: center;
position: relative;
--size:200px;
width: var(--size);
height: var(--size);
font-size: calc(var(--size)/2);
pointer-events: all;
}
label > .customCheckbox::selection{
/* prevent highliting the text within the custom checkbox */
background: none;
}
label > .customCheckbox:after {
z-index: -1;
content: "✔";
display: inline-flex;
justify-content: center;
align-items: center;
position: absolute;
width: 100%;
height: 100%;
box-sizing: border-box;
border-radius: calc(var(--size)/4);
border: solid black calc(var(--size)/10);
}
label > input:not(:checked) + div.customCheckbox:after {
background:#0000;
content: "\a0";
}
<input type="checkbox"/>
<label >
<input type="checkbox" class="hidden"/>
<div class="customCheckbox"></div>
</label >
I need the checkbox to be inside the label because I can't use the "for" attribute.
Thanks a lot for your help!
EDIT: for those wondering, here's the js solution (not ideal but since I can't do it in CSS, it's better than nothing) :
let checkboxes = document.getElementsByClassName("customCheckbox");
for(let i=0; i<checkboxes.length; i++){
checkboxes[i].addEventListener('mouseover', (e)=>{handleMouseOverCheckbox(e)})
checkboxes[i].addEventListener('mousedown', (e)=>{handleMouseOverCheckbox(e)})
}
function handleMouseOverCheckbox(e) {
e.srcElement.previousElementSibling.disabled = "true";
if (e.buttons == 1) {
e.srcElement.previousElementSibling.checked= !e.srcElement.previousElementSibling.checked;
}}
EDIT 2 : Here's the best solution I could come up with, thanks to
#zer00ne
codepen.io/DesignThinkerer/pen/bGVBLjM
A checkbox within a label is no problem. The problem arises when that checkbox is altered for the sake of accessibility instead of complete removal using display: none. When an interactive element like an input or button exists in the DOM, it will still be a factor no matter how its hidden unless display: none is applied.
In the Original Post the checkbox is almost impossible to click due to its 0px height and 1px width and yet when the div is clicked, the checkbox is clicked... sometimes not. Normally if the label was able to detect a click, that click would trigger a click event to the nested checkbox as well. In the OP code that's not happening because the label has pointer-events: none.
So the div is getting clicked and by some magical miracle this gains features that would not normally be attributed to it? Divs are not interactive they cannot affect elements that are not nested within themselves (i.e. like the checkbox that sits before the div). Nope, the div is useless, its the checkbox itself that's getting clicked due to the fact it is the only element within the inert label that gains focus by default. Gaining focus on an input doesn't necessarily guarantee a click event -- matter of fact a focus event selects an element and a click event sets an element as active. So what happens when a user double-clicks or moves the mouse quickly before the next click clears a label? Undesirable behavior as described in OP.
In the following demo, the checkboxes are hidden as per OP (also set width and height to 0) and removed pointer-events: none from the label and added it to the checkboxes. In this setup the label gains focus and click events and the click event will trigger the checkbox. The checkbox having been isolated from any extra clicks due to pointer-events: none and z-index: -1 should behave as expected.
As proof of concept I have added some JavaScript to demonstrate said code stability. The two event handlers are for demonstration purposes. The JS does not facilitate, stabilize, or modify performance of the HTML/CSS behavior.
On any change event on a checkbox (via label) will trigger function changeHandler() to gather all the values of the checked checkboxes and display them in an output.
If there's a checkmark in a box and there's a value displayed that corresponds to said checked checkbox, then it successfully passes as valid behavior.
Clicking button.show will trigger function clickHandler() to toggle the .reveal class to each checkbox.
While clicking rapidly observe that the revealed checkboxes are checked and its corresponding custom label is checked as well. Also notice that the value should also be displayed as well.
BTW
"....target doesn't work in IE IIRC"
event.target is the standard property to use in every modern browser. event.srcElement is a deprecated property used by IE which is almost entirely unsupported.
pointer-events: all assigned to input and .customCheckbox
The value all applies to SVG only. Only the values of none and auto are relevant to HTML. auto is default.
Demo
I cannot reproduce the described behavior except in the code provided in OP. If you can reproduce that behavior on my demo, please record a short video of it and post that and the machine/device, OS, and browsers (I will assume everything is reasonably up to date).
const main = document.forms.main;
main.onchange = checkHandler;
function checkHandler(e) {
const fc = this.elements;
const chx = [...fc.hidden];
const ui = e.target;
if (ui.matches('.hidden')) {
let text = chx.flatMap(c => c.checked ? [c.value] : []);
fc.view.value = '';
fc.view.value = text.join(', ');
}
}
main.onclick = clickHandler
function clickHandler(e) {
const fc = this.elements;
const chx = [...fc.hidden];
const ui = e.target;
if (ui.matches('button.show')) {
chx.forEach(c => c.classList.toggle('reveal'));
}
}
:root,
body {
--size: 10rem;
font: 400 small-caps 2vw/1.5 Times;
width: 100%;
height: 100%;
box-sizing: border-box;
}
*,
*::before,
*::after {
box-sizing: inherit;
font: inherit;
}
.display {
display: flex;
flex-flow: row nowrap;
justify-content: space-between;
align-items: center;
width: max-content;
max-height: min-content;
margin: 10px;
border: calc(var(--size) / 20) solid #000;
border-radius: 24px;
}
.view {
display: inline-block;
min-width: 35ch;
font-size: 1.5rem;
height: 1.5rem;
line-height: 1;
}
.show {
display: inline-block;
width: 12ch;
padding: 1px 3px;
margin: 4px;
border: 2px solid #000;
border-radius: 8px;
background: none;
text-align: center;
font-size: 1.25rem;
cursor: pointer;
}
.mask {
position: relative;
z-index: 2;
display: block;
width: var(--size);
height: var(--size);
padding: 0;
margin: 0 5px;
border: solid black calc(var(--size) / 10);
border-radius: calc(var(--size) / 4);
font-size: calc(var(--size) * 0.8);
background: none;
cursor: pointer;
}
.icon {
position: absolute;
z-index: 1;
top: -1.5rem;
right: 1rem;
display: inline-block;
margin: 0;
padding: 0;
border: 0;
}
.hidden {
position: relative;
z-index: -1;
display: inline-block;
width: 0;
height: 0;
margin: 0;
padding: 0;
border: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
pointer-events: none;
opacity: 0;
}
.reveal {
z-index: 0;
top: -24px;
left: 4px;
width: 1px;
height: 1px;
opacity: 1;
}
.icon::after {
content: attr(data-blank);
}
.hidden:checked+.icon::after {
content: attr(data-check);
}
<!DOCTYPE html>
<html>
<head lang='en'>
<meta charset='utf-8'>
<style></style>
</head>
<body>
<form name='main'>
<fieldset name='display' class='display'>
<output name='view' class='view'></output>
<button name='show' class='show' type='button'>Show</button>
</fieldset>
<fieldset name='display' class='display'>
<label name='mask' class='mask'>
<input name='hidden' class="hidden" type="checkbox" value='Check I'>
<fieldset name='icon' class='icon' data-check='✔' data-blank=' '></fieldset>
</label>
<label name='mask' class='mask'>
<input name='hidden' class="hidden" type="checkbox" value='Check II'>
<fieldset name='icon' class='icon' data-check='✔' data-blank=' '></fieldset>
</label>
<label name='mask' class='mask'>
<input name='hidden' class="hidden" type="checkbox" value='Check III'>
<fieldset name='icon' class='icon' data-check='✔' data-blank=' '></fieldset>
</label>
<label name='mask' class='mask'>
<input name='hidden' class="hidden" type="checkbox" value='Check IV'>
<fieldset name='icon' class='icon' data-check='✔' data-blank=' '></fieldset>
</label>
</fieldset>
</form>
<script></script>
</body>
</html>
So I'm trying to make custom slider that's cross browser (IE11, Chrome, Firefox, Edge) that's also a11y accessible and I'm having trouble getting the label element to respond to space bar for selection like a natural checkbox. I can do it easily with code of course but was curious if there's maybe something I'm missing with just the html/css I could do to accomplish the same thing.
As example see below, tabindex of course provides the visual and tabbing, but I can't seem to get the label to toggle it like a click would on label element. Should I just go the easy route and let some code handle it or does someone want to teach me something? Cheers!
// not yet, and yes I know I haven't added the aria attributes etc, it's just a quick PoC :)
main {
display: block;
margin: 0 auto;
padding: 2rem;
width: auto;
}
.slide-toggle {
margin: 0 3rem;
display: inline-block;
}
.slide-toggle:last-of-type {
margin-left: 0;
}
.slide-toggle input {
display: none;
}
.slide-toggle input:checked ~ label {
color: #fff;
background-color: skyblue;
}
.slide-toggle input:checked ~ label:after {
transform: translateX(100%);
}
.slide-toggle label {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
position: relative;
padding: .25rem 0;
cursor: pointer;
user-select: none;
border: #bbb 1px solid;
border-radius: 3px;
}
.slide-toggle label:after {
display: block;
content: '';
position: absolute;
top: 0;
bottom: 0;
left: 0;
width: 45%;
margin: .25rem;
border: #bbb 1px solid;
border-radius: 3px;
background-color: #ddd;
transition: transform 0.25s cubic-bezier(0.6, 0.82, 0, 0.76);
}
.slide-toggle label:focus {
box-shadow: 0 0 0 8px red;
}
.slide-toggle label:hover {
border-color: #777;
}
.slide-toggle label div {
flex-grow: 1;
flex-basis: 50%;
flex-wrap: nowrap;
text-align: center;
min-width: 1rem;
margin: .25rem 1rem;
}
<link href="https://use.fontawesome.com/releases/v5.8.2/css/all.css" rel="stylesheet"/>
<main>
<h2>Please click a slide toggle for example;</h2>
<div class="slide-toggle">
<input id="guidLater"
type="checkbox"/>
<label for="guidLater"
tabindex="0"
role="checkbox">
<div>YES</div>
<div>NO</div>
</label>
</div>
No Translation Option:
<div class="slide-toggle">
<input id="guidLater4"
type="checkbox"/>
<label for="guidLater4"
tabindex="0"
role="checkbox">
<div><i class="fas fa-check" style="color:green"></i></div>
<div><i class="fas fa-times" style="color:red;"></i></div>
</label>
</div>
</main>
Without getting too far into the specifics, as there are many, I'll just attack this from a pure solution perspective and provide some insight.
With HTML5 elements, the idea is that they have functionality assigned to them by default. For example, a checkbox inherits the behaviors that a checkbox should have because it's a default element. You also shouldn't be re-purposing elements for other uses if native ones are available as this breaks the first two rules of ARIA.
If you can use a native HTML element [HTML51] or attribute with the semantics and behavior you require already built in, instead of re-purposing an element and adding an ARIA role, state or property to make it accessible, then do so.
And
Do not change native semantics, unless you really have to.
The input types of Range (a slider) and Checkbox have two vastly different sets of keyboard behaviors, so i'm not surprised that this isn't working correctly.
I think what you mean to create is a "switch"
You shouldn't have display: none on the actual checkbox. You need to visually hide it, but still have it on the page because only then it would catch the space key press.
An easy (& suggested) way to do that is
.slide-toggle input {
width: 0;
height: 0;
}
also you don't need to have role="checkbox" and tabindex on the label.
I want to change the position of my label when the checkbox is checked. This works fine if I don't transition the top offset property of my label. However, if I add a transition to this value (see the commented code), click on the label and don't move the cursor of my mouse the label seems that is still on hover state. That means, even though I don't hover on it, the cursor is a pointer and the background-color green (hover state of label).
If you see the demo, you'll understand what I mean.
My HTML code is the following:
<section>
<input type="checkbox" id="checkbox_id">
<label for="checkbox_id">Click me</label>
<div>
This is the first link
This is the second link
</div>
</section>
My CSS:
* {
margin: 0;
}
section {
position: relative;
padding: 20px;
background: yellow;
}
input[type="checkbox"] {
display: none;
}
label, a {
height: 30px;
padding: 10px 0;
margin: 10px;
}
label {
display: block;
background: tomato;
cursor: pointer;
width: 100%;
position: absolute;
top: 0;
/*transition: top .3s ease;*/
}
label:hover {
background: green;
}
input[type="checkbox"]:checked + label {
top: 100%;
}
a {
display: block;
background: tomato;
}
a:first-child {
margin-top: 50px;
}
Any idea why that's happening?
So, a little bit of jQuery might help us out here. Take a look at this jsFiddle.
CSS change:
.label--hovered { background: green; }
instead of:
label:hover { background: green; }
i.e. converted it to a class.
JavaScript:
$(document).ready(function(){
$('label').hover(function(){
$(this).removeAttr('style').addClass('label--hovered');
}, function(){
$(this).css('cursor', 'default').removeClass('label--hovered');
}).click(function(){
$(this).trigger('mouseleave');
});
});
Does this help?
You are using the syntax for the transition property but on transform. Additionally, transform doesn't take a top value. It takes scale, rotation, skew etc. You probably want this:
transition: top .3s ease;
Also don't forget to add vendor prefixes. (ie. webkit).