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:
I am using <tbody> as a CSS selector to set the background-color in a table. I'm doing this because I have multiple <tbody> sections within the table, and they have different background colors.
My issue is that when using border-radius on the cells, the cell doesn't respect the background-color of the tbody. That is, the border radius cuts out the corners in the default background color (in this case white), not the tbody color (in this case, green), below.
UPDATE: This problem occurs in Chrome/Safari, but not in Firefox (just tested myself on all 3). Still looking for a workaround on Chrome (FOUND! See accepted answer).
tr:first-child td:first-child {
background-color: red;
border-radius: 25px;
}
table {
border-spacing: 0px;
}
table tbody {
background-color: green;
}
<table>
<tbody>
<tr>
<td><p>TOP LEFT</p></td>
<td><p>TOP RIGHT</p></td>
</tr>
<tr>
<td><p>BOT LEFT</p></td>
<td><p>BOT RIGHT</p></td>
</tr>
</tbody>
</table>
To be clear, the fix I'm looking for would change the resultant example so it looks like this (I'm just changing the table tbody selector to table only):
tr:first-child td:first-child {
background-color: red;
border-radius: 25px;
}
table {
border-spacing: 0px;
}
table { /* changed this line */
background-color: green;
}
<table>
<tbody>
<tr>
<td><p>TOP LEFT</p></td>
<td><p>TOP RIGHT</p></td>
</tr>
<tr>
<td><p>BOT LEFT</p></td>
<td><p>BOT RIGHT</p></td>
</tr>
</tbody>
</table>
I don't want to do it that way, because I want the background-color to be on the tbody (which I have multiple ones) NOT on the whole table.
Any way to make the tbody color show through?
Try making the <tbody> to render like a block element.
tbody {
background-color: green;
display: block;
}
tr:first-child td:first-child {
background-color: red;
border-radius: 25px;
}
table {
border-spacing: 0px;
}
tbody {
background-color: green;
display: block;
}
<table>
<tbody>
<tr>
<td><p>TOP LEFT</p></td>
<td><p>TOP RIGHT</p></td>
</tr>
<tr>
<td><p>BOT LEFT</p></td>
<td><p>BOT RIGHT</p></td>
</tr>
</tbody>
</table>
An updated answer for other users, if it helps.
On Chrome, the display: block fixes the issue. However, it causes other layout issues with the table, where it does not seem to respect widths. Using display: table instead seems to resolve both issues:
tbody {
background-color: green;
display: table;
}
Set cellspacing to 0, borders on the table to none, and collapse the table borders (to make sure there is not space around the colored boxes). Then apply the background color to the TD elements instead of the tbody element.
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 have a tab to be displayed on the bottom left corner of the window, but it seems to be hard to make it actually clickable (also the iframe is not clickable), so it does´t do any animation on cell phone and even web browser.
http://jsfiddle.net/bo9gy77q/3/
How could I fix this eror? In the console it says: Uncaught ReferenceError: $ is not defined. The problem with clicking the tab on mobile browsers, and make clickable the iframe?
Note: There is a iframe in the tab, so I also wil need it to be clickable, so far nothing that I tryed fix it.
Note: The hard to click is especially refered to old movile devices and safary.(so hard to make it work on those)
¿CAN I MAKE THE CLICKABLE ZONE BIGGER?
Have a look at this fiddle, animation is working now fine and smooth : http://jsfiddle.net/bo9gy77q/2/
I have just removed transition property from "#a-tab" class and smoothness is there
$("#a-tab,#a-tab *").click(function () {
//$("#a-tab").focus();
$("#a-tab").animate({
width: '320px'
}, "fast");
$(".deluxe").animate({
width: '30px'
}, "slow");
})
$("#a-tab").on('focusout', function () {
$("#a-tab").animate({
width: '10px'
}, "fast");
$(".deluxe").animate({
width: '5px'
}, "fast");
});
#a-tab:focus {
outline:none;
width:340px;
}
.deluxe {
position: relative;
background:#999;
width:5px;
height:108px;
}
#a-tab {
background:#FFF;
border:solid #d9d9d9 2px;
position: fixed;
bottom: 12px;
right: -20px;
z-index: 5;
width: 30px;
height: 112px;
padding: 5px 20px 5px 5px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
<div id="a-tab" tabindex="1">
<table>
<tbody>
<tr>
<td>
<div class="deluxe"></div>
</td>
<td></td>
<td>
<div class="g-page" data-width="273" data-href="//plus.google.com/u/0/111125806825828710565" data-layout="landscape" data-rel="publisher"></div>
</td>
</tr>
</tbody>
</table>
</div>
Right now I'm doing a td.click() and then window.location = td.find('a').attr('href') but it doesn't work if I'm clicking to make a new tab.
And I can't programmatically click the <a>.
Any ideas?
Feel free to fork this fiddle http://jsfiddle.net/uDQPr/
You could make the <a> fill up the entire cell. That way, you won't need any additional JavaScript to handle the click event. Adding target="_blank" to your <a> link will make it always open in a new tab (or a new window, for browsers that don't support tabs). Working example at http://jsfiddle.net/vTyAc/2/.
Here's the table code:
<table>
<tr>
<td>
Apple
</td>
<td>
YouTube
</td>
</tr>
</table>
And the CSS:
td {
border: 1px solid;
}
a {
text-decoration: none;
display: block;
width: 100%;
height: 100%;
padding: 10px
}
a:hover {
text-decoration: underline;
}