I have a div
whose overflow
property is set to scroll
in order to see all the contained fields without taking too much of the page's space. Each field has a span (the field's title) and input associated. I offer the possibility to the users to hover on the spans and see some useful information as a tool tip. I use the spans after
and before
pseudo-elements on the spans hover in order to add a customized tool tip. However, the tool tips display is restricted by the parent div overflow restriction.
Here is an example of the rendered HTML:
<div id="ContentPlaceHolder1_leftDiv" class="l-custom-left">
<div class="personalizedFields">
<table>
<tr>
<td>
<span title="Champ associé: Prenom" class="tooltip">Prénom</span>
</td>
</tr>
<tr>
<td>
<input name="ctl00$ContentPlaceHolder1$ucPF$ucCustomField36$field36" type="text" id="field36" />
</td>
</tr>
<tr>
<td>
<span title="Champ associé: Nom" class="tooltip">Nom</span>
</td>
</tr>
<tr>
<td>
<input name="ctl00$ContentPlaceHolder1$ucPF$ucCustomField37$field37" type="text" id="field37" />
</td>
</tr>
</table>
</div>
</div>
The css of the parent div and spans:
.l-custom-left
{
overflow-x: hidden;
width: 250px;
height: 50vh;
}
.tooltip:hover:after{
background: #333;
background: rgba(0,0,0,.8);
border-radius: 5px;
bottom: 26px;
color: #fff;
content: attr(title);
left: 20%;
padding: 5px 10px;
position: absolute;
z-index: 98;
}
.tooltip:hover:before{
border: solid;
border-color: #333 transparent;
border-width: 6px 6px 0 6px;
bottom: 20px;
content: "";
left: 50%;
position: absolute;
z-index: 99;
}
How can I allow my spans after and before content to "override" the div parent overflow restriction?
JSfiddle
If you really want this to work, there are two ways. The first is to use JavaScript (which I won't go into) to place an element outside the container at the position of the element. It's an obnoxious fix, and I've had to implement it too many times to count prior to the advent of CSS3.
The second is much more elegant, but does break semantics for HTML. The reason that your :before
and :after
pseudos won't pop out of a scrolling container is that they're treated like children of the .tooltip
element, and thus part of the document flow for the scrolling container even if they're positioned absolutely.
So how do we handle this? We cheat, plain and simple.
Add a second element after your span
like so:
<span class="tooltip">Prénom</span>
<span title="Champ associé: Prenom" class="tooltip-hovershim">Prénom</span>
We keep the same content so that the element will be the same size.
Then, alter your CSS like this:
.tooltip, .tooltip-hovershim
{
display: inline;
}
.tooltip:hover:after, .tooltip-hovershim:hover:after{
background: #333;
background: rgba(0,0,0,.8);
border-radius: 5px;
bottom: 26px;
color: #fff;
content: attr(title);
left: 20%;
padding: 5px 10px;
position: absolute;
z-index: 98;
}
.tooltip-hovershim {
position: absolute;
transform: translateX(-100%);
color: transparent;
}
.tooltip:hover:before, .tooltip-hovershim:hover:before {
border: solid;
border-color: #333 transparent;
border-width: 6px 6px 0 6px;
bottom: 20px;
content: "";
left: 50%;
position: absolute;
z-index: 99;
}
Et voila! Vous avez votre élément flottant. It's that simple.
Only applied to the first two elements for brevity's sake.
Just for shits and giggles, here's the Javascript variant
And here's the crucial portion of Javascript:
var span_labels = document.querySelectorAll('.personalizedFields td span'),
label_house = document.createElement('div');
document.body.appendChild(label_house);
label_house.setAttribute('class', 'tooltip-hoverer');
for (var i=0,l=span_labels.length;i<l;i++){
var curr_label = span_labels[i];
curr_label.addEventListener('mouseover', function(e) {
e.preventDefault();
label_house.innerHTML = e.target.getAttribute('title');
var xy = getOffset(e.target);
label_house.style.top = (xy.top - 26) + 'px';
label_house.style.left = (xy.left) + 'px';
label_house.style.display = 'block';
});
curr_label.addEventListener('mouseout', function() {
label_house.style.display = 'none';
});
}
function getOffset(el) {
var elementRectangle = el.getBoundingClientRect();
var _x = elementRectangle.left;
var _y = elementRectangle.top + el.offsetParent.scrollTop;
return { top: _y, left: _x };
}
Since what I wanted to do isn't feasible with only CSS, I decided to go with a mix of JavaScript and JQuery, inspired by Josh Burgess's JS solution. So if you were looking for a more JQuery oriented solution, here it is:
var tooltip;
$(document).ready(function() {
$('span.tooltip').on({
mouseenter: function (e) {
tooltip = document.createElement('div');
document.body.appendChild(tooltip);
$(tooltip).addClass('tooltip-hoverer');
$(tooltip).text($(this).attr('data-tooltip'));
var x = e.clientX - (($(tooltip).outerWidth()) / 2); // "($(tooltip).outerWidth() / 2)" is required to show the tooltip's down arrow next to the mouse's left position
var y = this.getBoundingClientRect().top + this.offsetParent.scrollTop;
$(tooltip).css({
top: y - 32,
left: x,
display: 'block'
});
},
mouseleave: function () {
document.body.removeChild(tooltip);
}
});
});
Note that unlike Josh's solution, I don't create all the tool tips at load, but only one each time a span is hovered, then I remove it when the cursor leaves the span.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With