I have an example of my current work here: https://jsfiddle.net/pv5xroLc/
My problem is that when the table in my example is fully scrolled to the right, the faded gradient still covers part of my table even though it cannot be scrolled further, thus it makes the last column harder to read. I am wondering what the best solution to hiding this gradient is while still making it clear that the table can be scrolled horizontally (this is going to appear on mobile).
Currently, my html structure is as follows:
<div class="fader">
<div class="scrollable">
*content*
</div>
</div>
The .fader element has an ::after pseudo-element which contains the "fader" on it, which is an absolutely positioned element with the linear-gradient I'm using to indicate that the element can be scrolled horizontally. The .scrollable element is a horizontally scrolling element that holds my table.
I currently have two solutions I have considered:
Add a listener to check when the scrollbar has reached the right side (like this example), then hide or fade out the gradient. The problem with this is that I have more than one of these faded tables on the page, and I'm not sure what the most effective way to setup these listeners would be. I'm using Vue.js with this, so I'm not sure if this could/should be a directive or just a listener set on the page for each of these tables.
Add some blank space to the right of the table, so you could scroll a little bit past the end of the actual table and the gradient would just blend into the background. I've tried adding padding and margins to both the table and the .scrollable element but it does not add any extra space after the table.
If anyone has suggestions for what they think I should do, it would be greatly appreciated.
If I needed to implement this functionality, I would make a Vue component that would take in the table content (or any content) in a slot and then listen to the scroll event of the .scrollable div, adding or removing the faded ::after content if the div was scrolled all the way to the right.
Here's an example:
Vue.component('fader', {
template: `
<div class="fader" :class="{ 'scrolled-right': isScrolledRight }">
<div class="scrollable" ref="scrollable">
<slot></slot>
</div>
</div>
`,
data() {
return {
isScrolledRight: false,
}
},
methods: {
onScroll(event) {
this.updateIsScrolledRight(event.target);
},
updateIsScrolledRight({ scrollLeft, offsetWidth, scrollWidth }) {
this.isScrolledRight = (scrollLeft + offsetWidth) === scrollWidth;
}
},
mounted() {
this.$refs.scrollable.addEventListener('scroll', this.onScroll);
this.updateIsScrolledRight(this.$refs.scrollable);
},
destroyed() {
this.$refs.scrollable.removeEventListeneer('scroll', this.onScroll);
}
})
.fader.scrolled-right::after {
opacity: 0;
}
Here's how the component works:
A ref property is added to the .scrollable div so that it can be easily referenced in the component's script.
An onScroll method is attached to the scroll event of the scrollable ref when the component is mounted and removed when the component is destroyed.
The onScroll method calls an updateIsScrolledRight method, passing it the scroll event's target (the .scrollable div).
The updateIsScrolledRight method looks at the scrollLeft, offsetWidth, and scrollWidth properties of the element passed as the parameter to determine if the element is scrolled all the way to the right and sets an isScrolledRight property to true if so and false if not.
The root div of the component has a bound :class attribute which will add the scrolled-right class to the div if the value of isScrolledRight is true.
The .scrolled-right class sets the div's ::after content to have opacity: 0;.
The updateIsScrolledRight method is also called in the mounted hook so that, if the content in the <slot> happens to not be wide enough to need a scrollbar, the fade will be removed in that case as well.
Here's a full working example:
Vue.component('fader', {
template: `
<div class="fader" :class="{ 'scrolled-right': isScrolledRight }">
<div class="scrollable" ref="scrollable">
<slot></slot>
</div>
</div>
`,
data() {
return {
isScrolledRight: false,
}
},
methods: {
onScroll(event) {
this.updateIsScrolledRight(event.target);
},
updateIsScrolledRight({ scrollLeft, offsetWidth, scrollWidth }) {
this.isScrolledRight = (scrollLeft + offsetWidth) === scrollWidth;
}
},
mounted() {
this.$refs.scrollable.addEventListener('scroll', this.onScroll);
this.updateIsScrolledRight(this.$refs.scrollable);
},
destroyed() {
this.$refs.scrollable.removeEventListeneer('scroll', this.onScroll);
}
})
new Vue({
el: "#app",
})
.fader {
position: relative;
width: 90%;
margin-left: 46px;
}
.fader::after {
content: "";
position: absolute;
z-index: 1;
top: 0;
right: -1px;
bottom: 15px;
pointer-events: none;
background: linear-gradient(to right, rgba(255, 255, 255, 0.1), white);
width: 10%;
opacity: 1;
transition: opacity .2s ease-out;
}
.fader .scrollable {
white-space: nowrap;
overflow-x: scroll;
position: relative;
}
.fader.scrolled-right::after {
opacity: 0;
}
.breakdown-title {
font-size: 14px;
font-weight: 700;
text-align: center;
margin: 8px auto;
}
table {
font-size: 12px;
margin: auto;
color: #000;
width: 100%;
table-layout: fixed;
}
table thead {
color: #fff;
background-color: #da291c;
}
table thead th {
width: 75px;
text-align: right;
}
table thead th:first-of-type {
width: 120px;
padding-left: 4px;
}
table thead th:last-of-type {
width: 80px;
padding-right: 4px;
}
table tbody tr:nth-of-type(odd) {
background-color: #fce9e8;
}
table tbody td {
width: 75px;
text-align: right;
}
table tbody td:first-of-type {
width: 120px;
text-align: left;
padding-left: 4px;
}
table tbody td:last-of-type {
width: 80px;
padding-right: 4px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<fader>
<div class="breakdown-title">Total Revenue Bonus</div>
<table>
<thead>
<tr>
<th></th>
<th>Oct</th>
<th>Nov</th>
<th>Dec</th>
<th>Jan</th>
<th>Feb</th>
<th>Mar</th>
<th>Apr</th>
<th>May</th>
<th>Jun</th>
<th>Jul</th>
<th>Aug</th>
<th>Sep</th>
<th>Year End</th>
</tr>
</thead>
<tbody>
<tr>
<td>YTD Target</td>
<td>$1,325,705</td>
<td>$2,651,410</td>
<td>$3,977,115</td>
<td>$5,302,821</td>
<td>$6,628,526</td>
<td>$7,954,231</td>
<td>$9,279,936</td>
<td>$10,605,642</td>
<td>$11,931,347</td>
<td>$13,257,052</td>
<td>$14,582,757</td>
<td>$15,908,463</td>
<td>$15,908,463</td>
</tr>
<tr>
<td>YTD Actual</td>
<td>$19,956</td>
<td>$19,956</td>
<td>$19,956</td>
<td>$19,956</td>
<td>$19,956</td>
<td>$19,956</td>
<td>$19,956</td>
<td>$19,956</td>
<td>$19,956</td>
<td>$19,956</td>
<td>$19,956</td>
<td>$19,956</td>
<td>$19,956</td>
</tr>
<tr>
<td>% to Target</td>
<td>2%</td>
<td>1%</td>
<td>1%</td>
<td>0%</td>
<td>0%</td>
<td>0%</td>
<td>0%</td>
<td>0%</td>
<td>0%</td>
<td>0%</td>
<td>0%</td>
<td>0%</td>
<td>0%</td>
</tr>
</tbody>
</table>
</fader>
</div>
(I'd rather comment on the OP than answer, but it won't let me start a comment yet because I'm too new so apologies for that.)
I've dealt with stuff like this before, and my quick hack following your #2 solution idea above would be to add another <th> after <th>Year End</th>, then style that with the appropriate width to match the gradient fade. You could then decide whether to also put in blank <td>s below it.
ALSO I noticed that your white gradient has a pretty solid white line where it starts (right above "$9") - you can smooth that out in line 15/16 of your SCSS by adding more reference points (it took me a while to figure this out back in the day, just including in case it's helpful):
background: linear-gradient(to right, rgba(255, 255, 255, 0), rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.2), white);
width: 30%;
Current hard line to white:
Softer transition to white:
Related
I'm trying to add a popup when a td is mousedover. Each row has multiple td's and the popup should only work on the first one. This works as long as mouseout is in the same column. That is, if I move the mouse up and down, the popup appears and disappears as expected. But if I move the mouse horizontally into the next td, the popup doesn't disappear. I created a jsfiddle for this but the popup isn't working. The console is saying the javascript function isn't defined but it works fine here so I must have something wrong in the jsfiddle setup. Here's the code I am using. Td's are being used because this is the code I was given. Can anyone see what is needed to get the popup to hide no matter how the mouse moved?
EDITED to solve the problem.
<style>
#pop-description{
display : none;
position : absolute;
z-index : 99999;
left : 0px;
padding : 10px;
background : #3AB9AE;
border : 1px solid #9a9a9a;
margin : 0px;
}
</style>
<script>
$(document).ready(function(){
function ShowDescription(id) {
var position = $('.class-desc-'+id).position();
var desc = $('#desc-'+id).val();
$('#pop-description').css('top', position.top);
$('#pop-description').text(desc);
//$('#pop-description').toggle();
$('.class-desc-'+id).mouseenter(function() {
$('#pop-description').css('display', 'block');
}).mouseleave(function() {
$('#pop-description').css('display', 'none');
});
}
});
</script>
<div style="display:relative;"><div id="pop-description" style="display:none;"></div></div>
<table>
<tr>
<td class="class-desc-0" onmouseOver="ShowDescription('0');">title</td>
<td>Address</td>
<td>State</td>
<input type="hidden" name="desc-0" value="first test" id="desc-0">
</tr>
<tr>
<td class="class-desc-1" onmouseOver="ShowDescription('1');">title</td>
<td>Address</td>
<td>State</td>
<input type="hidden" name="desc-1" value="second test" id="desc-1">
</tr>
<tr>
<td class="class-desc-2" onmouseOver="ShowDescription('2');">title</td>
<td>Address</td>
<td>State</td>
<input type="hidden" name="desc-2" value="third test" id="desc-2">
</tr>
</table>
I think you are overthinking it. Here is what I would do. I would use jQuery as demonstrated below.
Trigger the action you need on mouseenter
Initiate the opposite action on mouseleave
$(function() {
$(".toggle").mouseenter(function() {
// Your code goes below: initiate first action
$(this).addClass("showOff");
}).mouseleave(function() {
// Your code goes below: Initiate opposite action
$(".toggle").removeClass("showOff");
});
});
div {
cursor: pointer;
width: 200px;
height: 200px;
line-height: 200px;
text-align: center;
transition: all 2s;
}
.showOff {
width: 200px;
height: 200px;
line-height: 200px;
text-align: center;
background: orange;
transition: all 2s;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="toggle">Hove over me</div>
Note: In your case, you show the popup on mouseenter and hide it on mouseleave
Why not just using hover ? Like
class-desc:hover .popup{
display: block;
}
I need a layout with fixed column/row header. After some "trial and error" I came up with this solution (see jsfiddle). Only the lower right pane is scrollable, and the headers are moved using javascript.
Everything is fine in Chrome, but in IE/Edge the scrolling is laggy: the left and the right part are not scrolled on the same time but with a minimal delay (especially it the content is "big") which is annoying. Do you see any solution? I have to find another way to achieve the result (and in this case do you have any suggestion)?
$(function() {
init();
});
function init(){
var div1=$("#mainContainer");
var oldScrollTop = $(div1).scrollTop();
$(div1)
.scroll( function () {
if (oldScrollTop == $(div1).scrollTop()){
scrollRowTableHeader(); //horizontal scroll
}else {
oldScrollTop = $(div1).scrollTop();
scrollColumnTableHeader(); //vertical scroll
}
});
}
function scrollColumnTableHeader() {
var vScrollPanel = $("#mainContainer"),
headerPanelContent = $("#headerPanelContent"),
colHeaderPanelContent = $("#colHeaderPanelContent");
var colHeaderPanelContent2 = document.getElementById('headerContent');
colHeaderPanelContent2.style.top = (0 - vScrollPanel.scrollTop())+"px";
};
function scrollRowTableHeader() {
var vScrollPanel = $("#mainContainer"),
headerPanelContent = $("#headerPanelContent"),
colHeaderPanel = $("#colHeaderPanel");
var headerPanelContent2 = document.getElementById('headerPanelContent');
headerPanelContent2.style.left = (0 - vScrollPanel.scrollLeft())+"px";
};
#container {
/*background-color: green;*/
display: flex;
}
.item {
background-color: white;
border: 1px solid black;
flex-grow: 0;
}
.bigContent{
height: 1000px;
width: 1000px;
}
.scroll{
overflow: auto;
height: 300px;
width: 500px;
}
.colheader{
height: 300px;
width: 200px;
/*position: relative;*/
z-index: 1;
overflow:hidden;
display: inline;
border: solid 1px;
}
.headerContent{
background: lightgrey;
height: 1000px;
width: 200px;
position: relative;
border: solid 1px;
}
.rowHeaderContent{
background: whitesmoke;
height: 100px;
width: 200px;
position: relative;
}
.rowColHeaderContent{
background: lightyellow;
height: 100px;
width: 200px;
border: solid 1px;
}
.headerPanel{
overflow: hidden;
width: 500px;
border: solid 1px;
}
.tableProperties{
table-layout: fixed;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div id="container">
<div id="header" class="header">
<div class="rowColHeaderContent">
<table class="tableProperties">
<tr><td>Row/Col Header</td></tr>
</table>
</div>
</div>
<div id="headerPanel" class="headerPanel">
<div id="headerPanelContent" class="rowHeaderContent">
<table width="500px" class="tableProperties">
<colgroup><col width="50"><col width="40"><col width="40"><col width="40"><col width="45"><col width="40"></colgroup>
<tr><td>Some content (header)</td><td>Some content</td><td>Some content</td><td>Some content</td><td>Some content</td><td>Some content</td></tr>
</table>
</div>
</div>
</div>
<div id="container">
<div id="colheader" class="colheader">
<div id="headerContent" class="headerContent">
<table width="500px" class="tableProperties">
<tr><td>Column Header</td></tr>
<tr><td>Column Header</td></tr>
<tr><td>Column Header</td></tr>
<tr><td>Column Header</td></tr>
<tr><td>Column Header</td></tr>
<tr><td>Column Header</td></tr>
</table>
</div>
</div>
<div id="mainContainer" class="item scroll">
<div class="bigContent">
<table width="500px" class="tableProperties">
<colgroup><col width="50"><col width="40"><col width="40"><col width="40"><col width="45"><col width="40"></colgroup>
<tr><td>Some content</td><td>Some content</td><td>Some content</td><td>Some content</td><td>Some content</td><td>Some content</td></tr>
</table>
</div>
</div>
</div>
So, this is not a direct answer to your really complex issue, but here goes..
First of all looks like you are in the first steps of trying to create a fixed-column and fixed-row table. It might seem to be working at first, but, trust me, the way you are trying to achieve it is not going to work well and the reason is that since table-cell widths are set arbitrarily for each table, you won't be able to align the header table with the content table below it. So, you will either have to follow a different approach than the current way you are taking - tips imminent.
First things first, the problem with a lot of data in this kind of situation is more noticeable in IE11 - the lag is lighter in IE edge and there is no lag in other browsers. In your case you can use debouncing in order to reduce the js calls while scrolling.
setInterval(function(){
// Scroll stuff here
}, 2)
The interval doesn't have to be high, a light lag will be visible, but it will be a great improvement compared to how (I suppose) it currently works.
Also try using Javascript functions that work with minimum impact, eg use .attr instead of .style; also try using CSS3 transforms instead of normal transforms.
So concerning the different approach - you have to somehow the scrolling header and content be in the same table - ideally the fixed column as well. Here are some tips. It is necessary to remove word wrapping within the table cells by using white-space:nowrap for the following tips to work as intended. The concept might be difficult to grasp, but once you encounter each issue they will make sense.
First of all add some top padding to the table that will accommodate the header elements described next. Create both p and span elements inside the header cells and place the same header content in both of them. Absolutely position the span elements using CSS3 transforms towards the top.
The 'p' elements will remain static, but not visible - hide them using visibility, opacity and height:0. Paragraph elements will be used in case the th content exceeds the td content.
In order to scroll everything with decent performance you will HAVE to use CSS3 transforms to scroll the span elements (careful: the span elements!) after you cache them on page load.
Hope these tips help you out, for any questions feel free to ask - it will be difficult to digest at first but it will make sense.
If you want Only the lower right pane is scrollable?
I just used css like :-
.headerContent{
background: lightgrey;
height: 1000px;
width: 200px;
position: static;
border: solid 1px;
}
Changed position: relative into static...
I'm trying to emulate hovering over an element with a mouse, using jQuery.
This is different from adding :hover to the element; I want something similar in function to using $(element).click(), however doing $(element).hover() doesn't work for me.
The element in question is (as far as I can see) using the jQuery UI datepicker with a tooltip on hover; for a live example, see an AirBnB listing, click the "dates" calendar input on the right hand side and hover over an available date.
I want to trigger the hover over each available date to get the price to hover above, although doing:
$('.ui-datepicker.ui-widget .ui-datepicker-calendar:eq(0) tbody tr td:not(.ui-datepicker-unselectable)').each(function(){
$(this).hover()
})
or simply
$('.ui-datepicker.ui-widget .ui-datepicker-calendar:eq(0) tbody tr td:not(.ui-datepicker-unselectable)')[0].hover()
doesn't work for me, nor does using mouseover(). Any idea how I can replicate this behaviour?
You should try trigger-ing the event:
$("element").trigger('mouseenter');
Also look at this post on SO, looks very similar to yours.
Well, you can do this just with CSS, here's a simplified example:
.td-hover td {
position: relative;
width: 1em;
border: 1px solid #ddd;
}
.on-hover {
display: none;
position: absolute;
top: -1.5em;
left: -1em;
background: #eee;
border: 1px solid black;
}
.td-hover td:hover .on-hover {
display: inline-block;
}
<table class="td-hover">
<tbody>
<tr>
<td>1<span class="on-hover">one</span></td>
<td>2<span class="on-hover">two</span></td>
<td>3<span class="on-hover">three</span></td>
<td>4<span class="on-hover">four</span></td>
<td>5<span class="on-hover">five</span></td>
<td>6<span class="on-hover">six</span></td>
<td>7<span class="on-hover">seven</span></td>
</tr>
<tr>
<td>8<span class="on-hover">eight</span></td>
<td>9<span class="on-hover">nine</span></td>
<td>10<span class="on-hover">ten</span></td>
<td>11<span class="on-hover">eleven</span></td>
<td>12<span class="on-hover">twelve</span></td>
<td>13<span class="on-hover">thirteen</span></td>
<td>14<span class="on-hover">fourteen</span></td>
</tr>
</tbody>
</table>
But if you insist on using JavaScript instead, just use jQuery's hover to add/remove a class:
$(".td-hover td").hover(
function() {
$(this).find(".on-hover").addClass("showing");
},
function() {
$(this).find(".on-hover.showing").removeClass("showing");
}
);
.td-hover td {
position: relative;
width: 1em;
border: 1px solid #ddd;
}
.on-hover {
display: none;
position: absolute;
top: -1.5em;
left: -1em;
background: #eee;
border: 1px solid black;
}
.on-hover.showing {
display: inline-block;
}
<table class="td-hover">
<tbody>
<tr>
<td>1<span class="on-hover">one</span></td>
<td>2<span class="on-hover">two</span></td>
<td>3<span class="on-hover">three</span></td>
<td>4<span class="on-hover">four</span></td>
<td>5<span class="on-hover">five</span></td>
<td>6<span class="on-hover">six</span></td>
<td>7<span class="on-hover">seven</span></td>
</tr>
<tr>
<td>8<span class="on-hover">eight</span></td>
<td>9<span class="on-hover">nine</span></td>
<td>10<span class="on-hover">ten</span></td>
<td>11<span class="on-hover">eleven</span></td>
<td>12<span class="on-hover">twelve</span></td>
<td>13<span class="on-hover">thirteen</span></td>
<td>14<span class="on-hover">fourteen</span></td>
</tr>
</tbody>
</table>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
I stumbled upon this plugin
https://jquery-datatables-row-grouping.googlecode.com/svn/trunk/customization.html
the problem is, everything is in <tr>s and I fear you cannot animate them?
Is there really no way? CSS or javascript wise.
e.g. I want to animate a tables tr elements.
Maybe one solution could be like that :
var animate = function(evt) {
//we go to the 'tr' tag
$el = $(evt.currentTarget).parent().parent();
//we hide the 'td's tag
$el.find('td').hide(333 , function(){
// we reduce the height of 'tr' tag
$el.animate({height : 0} , 777)
});
}
$('button').click(animate);
table {
border: solid 1px #AAA;
padding: 3px;
border-collapse: separate;
}
td {
height: 50px;
margin : 3px;
min-width : 50px;
border: solid 1px orange;
}
tr {
height : 55px;
padding : 12px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
<tr>
<td>a</td><td>b</td><td><button>hide</button></td>
</tr>
<tr>
<td>a</td><td>b</td><td><button>hide</button></td>
</tr>
<tr>
<td>a</td><td>b</td><td><button>hide</button></td>
</tr>
</table>
I want my tooltip element (the <span>) to appear above everything on the page but still relative to its parent element (the <td>). I'm trying with JS but would like a no-script solution.
JS to show/hide the <span>:
window.AETid = "AE";
function tooltip(tid) {
document.getElementById(tid).style.display="block";
}
function hideTooltip(tid) {
document.getElementById(tid).style.display="none";
}
HTML:
<td class="ht" onmouseover="tooltip(window.AETid);" onmouseout="hideTooltip(window.AETid);">
Send reminders?
<span class="tooltip" id="AE">If this option is selected, e-mail reminders will be sent to the client before each appointment.</span>
</td>
CSS for .tooltip:
.ht { position: relative; }
.tooltip {
color: #ff0000;
display: none;
z-index: 100;
position: absolute;
right:0px;
bottom: 0px;
}
Currently, the tooltip appears as expected when I hover over the <td>, but it appears within the element, thus changing the size of the <td> and thus the <tr> and thus the whole dang <table>. I want the tooltip to appear, well, like tooltips do: above and not effecting the rest of the page. z-index doesn't seem to do it alone in my case...
Using position: fixed instead of absolute on the tooltip <span> kept the element from interrupting the DOM, but literally positioned it after everything else on the page (at the bottom)
All help is greatly appreciated
I found a method to make a very lightweight tooltip with no JS!
.ht:hover .tooltip {
display:block;
}
.tooltip {
display: none;
color: red;
margin-left: 28px; /* moves the tooltip to the right */
margin-top: 15px; /* moves it down */
position: absolute;
z-index: 1000;
}
<table>
<td class="ht">Send reminders?
<span class="tooltip">this is the tooltip alshdgwh gahfguo
wfhg fghwoug wugw hgrwuog hwaur guoarwhg rwu</span>
</td>
</table>
Totally awesome and props to this guy!
just use abbr tag ?
<p><abbr title="World Health Organization">WHO</abbr> was founded in 1948.</p>
<p title="Free Web tutorials">W3Schools.com</p>
Just curious what is wrong with the title attribute of the td???
<td title="If this option is selected, e-mail reminders will be sent to the client before each appointment.">
Send reminders?
</td>
Solution for tooltip ON TOP (always even if no space available)
.ht:hover .tooltip {
display:block;
}
.ht{
position: relative;
}
.tooltip {
display: none;
color: red;
z-index: 1;
position: absolute;
top: -50%;
transform: translatey(-50%);
}
<br><br><br><br>
<br>
<br>
<div class="ht">Send reminders? <br/> xaxaxa <br/> xaxaxa <br/> xaxaxa
<span class="tooltip">this is the tooltip alshdgwh gahfguo
wfhg fghwoug wugw hgrwuog hwaur guoarwhg <br/> sdfaasdfrwu<br/> sdfaasdfrwu<br/> sdfaasdfrwu</span>
</>
Try this its simple and compact,
I made it myself
.info:hover .tooltip {
color: red;
visibility: visible;
opacity: 1;
transition: opacity 1s
}
.tooltip {
visibility: hidden;
opacity: 0;
transition: opacity 1s
}
}
.tooltip:hover {
visibility: visible
}
.info {
cursor: help
}
<span class="info">Text<span class="tooltip">MSG</span></span>