How to lay an image over an image in table cell - css

I'm trying to lay an image on top of an image in a table cell (if you click on it).
<table>
<tr>
<td style="background-color:Gray;"><a id="firstcell" onclick="addImage"><img scr="snowman.png"></a></td>
</tr>
...
</table>
Here is the addImage function:
function showMoves(fig_name,pos) {
var image = document.createElement("img");
var imageParent = document.getElementById("firstcell");
image.src = "circle.png";
image.style.position = "absolute";
imageParent.appendChild(image);
}
I tried it with absolute position, but it is on the right side of the table cell and not directly on top.
I clicked on the Lightgray cell and it place it over the gray one.
The important thing is to add the circle with a function that is executed when you click on the snowman and the circle has to be above. Or is there a different way to mark cells in a table?

It's much easier to assign a class to the image and then style that class as per the code below:
window.onload = () => {
document.getElementById('firstcell').addEventListener('click',addImage); //I've moved this from the 'onclick' attribute in your html to the window.onload event. It's better practice.
}
function addImage() {
var image = document.createElement("img");
var imageParent = document.getElementById("firstcell");
image.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAAAAABVicqIAAAALHRFWHRDcmVhdGlvbiBUaW1lAFdlZCAyNSBKYW4gMjAyMyAxNzozMTozNyAtMDAwMLY8BBsAAAAHdElNRQfnARkRICUUgImdAAAACXBIWXMAAAsSAAALEgHS3X78AAAABGdBTUEAALGPC/xhBQAAAAJ0Uk5TAP9bkSK1AAAB3ElEQVR42u2a0Q7DIAhFhf//5y5L1lUUFZTrtmY8LJmKx0trq1I6ksfo+eNzeXqZPUj+9ZBsENKLraAxhPrVFtAIogWpxI7H2WtB/X4G1SaIoQ8jpgmx+luatSBkioMEtQUfU26+IakQjwzLqDSInzFwqiHeUBkoFWRKxsCzhMwz3s61bwFpNVujsNJ0mvHyrB6pEjL3ThpSBGSZ0aDkELL3NaJ0lKwLSeorJ4MEBKtBuSAhwdKDwYP6acvHzKIwiFF2w73KKClcF8VL4XbVKuUaN8sKjL0gsdEqpfBCR04l4UI0yCkw2M6xw8KVj5m1wmjjXBZKCtdFgUYSArVtEPAsgSshNOS6l2514Slh5/vNwvWHuCBy+fLLSn4aoq4gEXa8IdhnCl7JByCYiXJkEMhFUXZa0DkPuybq7hch5bwMLP6BbOctHC5FrE7KAxykkmiKXGZh9iedk7vYgCk7LT3HEyOkvIUxJ2uIfXzvXDgqYPXxHKtNYhnx513aIOvjwTUpakpAhmuZoqcduNkyjvGRJM2edNOexNmsGF8KcIriT2buScs6xUwmmFM+WXCpcpv/atJf9lH106lyQpLtEbP2IYYFE/FJSRcU93FMAxT/mc+i3W/3+4d8D+QBTk2Hi2UsNw8AAAAASUVORK5CYII="; //this is just a png of a circle encoded in to base 64 so it'll work on this site
image.className = "overlay"; //rather than using inline styles, just set the classname to overlay and then you can use as much css as you like to get the effect you want.
imageParent.appendChild(image);
}
img {
display: block; /* remove that annoying space at the bottom of inline items */
}
td {
position: relative; /* this creates a new containing block so we can position the overlay image relative to the table cell */
}
.overlay {
position: absolute;
left: 3px; /* 3px from the left of the table cell and... */
top:5px; /* 5px from the left of the table cell and... */
}
<table>
<tr>
<td style="background-color:Gray;"><a id="firstcell"><img src="https://picsum.photos/id/64/100/100"></a></td>
</tr>
</table>

Related

Width of Rotated Table Headers in DOMPDF

I have a big table with lots of columns that I need to fit into a PDF page using DOMPDF. Just like here, my headers are much wider than the corresponding content, so I'm trying to rotate them. The HTML opened in Firefox seems ok, but the resulting PDF is not.
The Setup
HTML:
<td class="cell rotated_vertical_td" style="width:3%;">
<div class="rotated_vertical">Rotated_</div>
</td>
The width attribute above is being calculated in PHP based on the total number of columns.
CSS:
.cell {
font-size: 8pt;
}
.rotated_vertical_td {
height: 280px;
width: 20px;
text-transform:uppercase;
margin:0;
padding:0;
}
.rotated_vertical {
-webkit-transform:rotate(270deg);
-moz-transform:rotate(270deg);
-ms-transform:rotate(270deg);
-o-transform:rotate(270deg);
transform:rotate(270deg);
transform-origin: 50%;
width: 20px;
}
I had to apply the .cell class to each <td> because DOMPDF was not picking up the table td rule for some reason.
The Problem
It seems like DOMPDF first renders the text, changes the table cell accordingly, and then rotates it. Which means that the column still takes as much space and that breaks the whole point.
I've tried using substr() to cut the text to be only 2, 4 or 8 characters long. Looks like the column widths are adjusting accordingly.
Those are the screenshots of the actual PDF being rendered. As you can see, the last one fits less columns, even though the markup and the CSS is the same. Only thing thats changed is the characters count. Looks like it completely ignores the width I set on those headers.
In case of HTML, it looks like that width: 20px; on the inner div makes a difference - if I remove it, the HTML headers become wide as well.
So again, it looks like the inner div width stretches the table header cells. I can override that width and it works for HTML, but it does not for PDF.
What makes it worse is that DOMPDF does not seem to support having multiple page orientations in a single document so I can't have that page in the landscape mode.
In an answer to this question it is advised to use the absolute positioning, but I am not sure how that'd work with DOMPDF. For example, they treat the position:fixed elements as page headers, might have something reserved for the absolute positioned ones.
Please help
Update 2015-01-26
Thanks to BrianS for his help, I've managed to make the text rotate using the CSS approach he suggested and the latest DOMPDF downloaded from GitHub. Before that, I was generating the dynamic images with rotated text for each one of the headers.
Several things I'd like to point out for those who got here searching for the solution (including the future me I guess)
Positioning the content is a nightmare. There seems to be no way to predictably control the position and behaviour of the rotated headers neither in Firefox, nor in DOMPDF, and that is frustrating. First I had to change the transform-origin property to be:
transform-origin: left bottom 0;
... just so it's position is less random because otherwise changing either top/left or width/height properties kept moving the block in both dimensions. The fact that there are about 7 variables to control (top,left,width/height of the :after element, as well as the line-height and width/height of the wrapping cells) makes it impossible to go through all possible combinations of those to get the desirable position.
If a header is too long and contains any breakable characters - such as spaces - it gets split into several lines and those lines get combined into one by overlaying each other. Please see:
I couldn't find a way to prevent that. Seeing that the text that doesn't contain any breakable characters is still being placed on one line,
tried playing with the width/height/line-height controls
tried replacing the spaces with inside of the css content property but that gets printed directly and does not act as the non-breakable space
tried using the ASCII characters as advised here but it outputs some weird characters instead
replaced the spaces with underscores - it works but looks ugly
so I've tried replacing the spaces with <span style="color:#fff">_</span> so this way the color of the underscore matches the background, but again, that whole thing gets printed directly, and I've also realised that that span would break the line anyway
it feels like that line that gets combined just doesn't have enough width, but if I change the width of the .rotate .content:after element, it just moves that overlayed line to the right, although when I open the HTML in Firefox that seems to help
So for now, my solution is using the underscores, but that doesn't look professional. I'd appreciate if you could help me out with a soution.
Here's the updated setup:
HTML:
<tr class="table_summary_thead">
<td class="rotated_vertical_td" style="width:3%;">
<a hred="appendix_item_44">
<div class="rotated_vertical_outer">
<div class="rotated_vertical_inner rtb1f73b0b57649457abd0ca2e0e8c94e0f7d79c25">
</div>
</div>
</a>
<style type="text/css">
.rtb1f73b0b57649457abd0ca2e0e8c94e0f7d79c25:after {
content:"COMPREHENSIVE";
}
</style>
</td>
</tr>
CSS:
.table_summary {
width:100% !important;
}
.table_summary td {
text-align:center;
}
.table_summary tbody td {
font-size: 10pt;
padding: 4px;
}
.rotated_vertical_td {
width: 20px !important;
height: 680px !important;
font-size: 8pt;
margin:0;
padding:0;
text-align:left;
}
.table_summary_thead {
line-height: 220px;
text-align:left;
}
.rotated_vertical_outer {
position: relative;
overflow: visible;
text-align:left;
}
.rotated_vertical_inner:after {
height: 150px;
overflow: visible;
position: absolute;
text-align: left;
transform: rotate(270deg);
transform-origin: left bottom 0;
width: 20px;
top: 100px;
left: 110px;
}
Including most of the CSS here because who knows, some small part that seems irrelevant might make a difference.
There's a lot to cover in this question. In the future you might want to focus a bit more. Let's get started.
First, your impression of how dompdf deals with tables is correct. Cells are rendered before transforms are applied. Actually, I'm pretty sure right now that transforms have no effect on the flow of the document, just the appearance of the transformed content.
Second, it is true that dompdf does not currently support multiple page orientations. If you want to use dompdf for that you'll have to create each PDF separately and use something like pdftk or fpdf/fpdi to combine the results.
Third, position: fixed isn't treated as page headers but as the CSS spec outlines, i.e. persistent across pages. position: absolute is also treated per the spec (for the most part). Absolutely positioned content is placed at the specified coordinates according to either a) the page where it is encountered or b) the first parent element without static positioning.
If you set a parent element to position: relative and then absolutely position one of it's children the child element will be positioned relative to the parent. This is why the last referenced question recommends that styling. It should work in dompdf, except that dompdf's wonky table handling is resulting in the wrong column width.
So how to work around the issue? If you leave the content out of the HTML then dompdf will render it the width you specify. You can add the content back in using CSS. The following seems to work well enough:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<style type='text/css'>
td {
width: 20px;
}
.rotate .container {
position: relative;
overflow: visible;
}
.rotate .content:after {
width: 150px; height: 150px;
overflow: visible;
content: "Overall Satisfaction";
transform: rotate(-90deg);
transform-origin: center center;
position: absolute; left: -150px; top: -200px;
}
</style>
</head>
<body>
<table border="1">
<thead>
<tr style="line-height: 200px;">
<th><div>Facility</div></th>
<th><div>Date</div></th>
<th><div>Score</div></th>
<th class="rotate"><div class="container"><div class="content"></div></div></th>
</tr>
</thead>
<tbody>
<tr>
<td>Los Angeles</td>
<td>11/12/2010</td>
<td>3.5</td>
<td>2.5</td>
</tr>
<tr>
<td>San Diego</td>
<td>11/17/2010</td>
<td>10.0</td>
<td>10.0</td>
</tr>
</tbody>
</table>
</body>
</html>
Make sure you're using dompdf 0.6.1.
I had the same problem and tried out the answer of BrianS, which worked perfectly - for the first page.
However, when the table spanned several pages (with the header being repeated), the rotated texts on all following pages were positioned wrongly.
Thinking a bit more about this, I found a surprisingly simple solution IF, LIKE ME, YOU WANT THE ROTATED TEXTS IN EXACTLY THE SAME POSITION ON ALL PAGES (if not, then this will not work): Just don't draw the texts at all during the PDF creation, but instead add them later on like the page numbering.
That is, simply create the cells with the needed height and width but leave them empty in the actual HTML layout.
Then add the texts afterwards like this:
// assuming you loaded the HTML,
// create the PDF and grab the canvas
$dompdf->render();
$canvas = $dompdf->get_canvas();
// add rotated text!
$font = Font_Metrics::get_font("helvetica");
$canvas->page_text(250, 50, "Rotated", $font, 9, array(0,0,0), 0, 0, 270);
$canvas->page_text(300, 50, "Text", $font, 9, array(0,0,0), 0, 0, 270);
The last parameter here is the angle of rotation. Attention: There seem to be different dompdf versions out there, with some of them having two spacing parameters between the color and the rotation (like shown here), and some of them having one spacing parameter between the color and the rotation.
First, set height of A_LONG_HEADER height:125px and set left and right margin in inside div margin-left: -50px margin-right: -50px. The following seems to work well:
HTML
<table id="codexpl">
<tr >
<th >#</th>
<th><span >98</span></th>
<th id="rotate"><div id="vertical">A_LONG_HEADER</div></th>
</tr>
<tr>
<td>1</td>
<td>This</td>
<td>c</td>
</tr>
<tr>
<td>2</td>
<td>6</td>
<td>two</td>
</tr>
<tr>
<td>3</td>
<td>is</td>
<td>not</td>
</tr>
<tr>
<td>4</td>
<td>the</td>
<td>Column</td>
</tr>
<tr>
<td>5</td>
<td>first</td>
<td>One</td>
</tr>
</table>
<p>TEST</p>
CSS
#rotate
{
height:125px;
}
#vertical
{
-webkit-transform:rotate(-90deg);
-moz-transform:rotate(-90deg);
-o-transform: rotate(-90deg);
margin-left: -50px;
margin-right: -50px;
}
Insert info in different places per page can be implemented by inline PHP:
$header = array();
foreach($participants_t as $p_id => $p) {
$header[] = $p['Title'];
}
...
$dompdf = new DOMPDF();
$html = htmlspecialchars_decode(htmlentities($html, ENT_NOQUOTES, 'UTF-8'), ENT_NOQUOTES);
$dompdf->load_html($html);
$dompdf->set_paper("A4", "landscape");
$dompdf->render();
$pdf = $dompdf->get_canvas();
$GLOBALS["header"] = $header;
if (isset($pdf)) {
$pdf->page_script(
'$plus_top = 70; '
. 'if ($PAGE_NUM > 1) { '
. '$plus_top = 0; '
. '} '
. '$font = Font_Metrics::get_font("DeJavu Sans"); '
. '$header = array(); '
. '$header = $GLOBALS["header"]; '
. 'foreach($header as $key => $item) { '
. '$pdf->text(122 + 22.01 * $key, 178 + $plus_top, $item, $font, 9, array(0,0,0), 0, 0, -90); '
. '} '
. '');
}
return $dompdf;
So at first page ($PAGE_NUM = 1) top position is 70px bigger.
This method has one problem - font subset isn't created for text inserted by inline PHP.

Match height of absolute td

I am trying to setup a frozen column and the only problem left I have to solve is the heights of the other td's on the same row do not expand to match the height of the absolute positioned td. Since the text in the frozen header is arbitrary, it could span multiple lines. If it weren't absolute positioned then this would force the other td's in that same row to assume the larger height, which is the behavior I would like. But since that first td is absolute, then the heights are no longer linked to the other td's in the same row.
How do I force the other td's in that same row to assume the larger height of the first td?
http://jsfiddle.net/2Wrms/5/
table {
border-collapse: separate;
width: 100%;
table-layout:fixed;
}
td {
margin:0;
}
div.pivot-container {
width: calc(100% - 125px);
overflow-x:scroll;
margin-left:125px;
overflow-y:visible;
}
.headcol {
position:absolute;
width:125px;
left:20px; /*must match padding of outer element*/
top:auto;
}
.long {
width: 230px;
vertical-align:top;
}
<div style="padding-left:20px;">
<div class="pivot-container">
<table>
<tr>
<td class="headcol"> kjgh kj ghkj ghkj gkj g2jhfgjhfg</td>
<td class="long">QWERTJKLZXCVBNM</td>
<td class="long">QWERTYUIOFGHJKLZXCVBNM</td>
<td class="long">QWERTYUVBNM</td>
<td class="long">QWERTYCVBNM</td>
</tr>
<tr>
<td class="headcol">5765765785</td>
<td class="long">QWERTYUIOPXCVBNM</td>
<td class="long">QWERTYCVBNM</td>
<td class="long">QWERTYCVBNM</td>
<td class="long">QWERTYUIOPASDNM</td>
</tr>
</table>
</div>
</div>
You want the other rows to have the same height as the cells in the first column, right? If I understood correctly, you will need to establish the width of the whole table using jQuery, so that the height will change to what you want too.
If you want, for instance, that all rows be no taller than 26px (the height of the cells in the first column), then the table's width can be changed to match this limit by iterating through the rows and checking the height of each, and changing the width accordingly, since just indicating it in the css that all rows should be X height will not work.
$(window).load(function() {
var cont = 0;
var control = false;
var wTable = $("table").width();
do {
cont = 0;
$("tr").each(function() {
h = $(this).height();
if(h > 26) {
cont++;
if(cont == 1) {
return false;
}
}
});
if(cont == 0) {
control = true;
}
else {
wTable = wTable + 200;
$("table").width(wTable);
}
} while(!control);
$("tr").height(26);
});
This way, we're telling the table to add 200px to its width for every row that it finds taller than 26px, and once there are no more, it will stop. At the end of the loop we establish the height one more time in case some rows ended up being shorter than the first column.
You can see the example here, which uses a bit of your original code (I'd recommend testing it directly in your browser, however, since due to JSFiddle's layout it will not display correctly). Hope this helps.
The solution I found was to repeat the content from the absolutely positioned td into the last td of the relative td's. Since I can generate the columns server side, it was easy to repeat the content in another column. I then used css to set `visibility:hidden' on that last td. As long as the width of both that hidden td and the absolute td are the same, then they will have the same height, and those both parts of the row, the absolute and relative, will have the same height.
It has a negative side affect of still taking up space at the end of the table, so it might affect the sizing of outer elements.

Making the text vertical-aligned inside a Gridster's box doesn't work

I'd like to have a text inside a Gridster grid and make it text-align: center and vertical-align: middle.
<li><span>Text</span></li>
I tried this:
.gridster .gs_w > span {
text-align: center;
vertical-align: middle;
}
Only the text-align works. I can't use line-height because every grid's height is different based on what the user choose.
I assume because it does not have a height and width directly, that's why margin: auto 0px; doesn't work either. They got their height and width via data attributes.
Does anyone know Gridster's css behavior? How could I vertically center the elements (image or text) inside a grid?
This is how I add elements via Ajax
$.getJSON( "data/tiles.json", function( json ) {
for(i=0; i<json.length; i++) {
gridster.add_widget(
'<li class="gs_w" id="'+count+'" style="background-color: '+json[i].rgb+'"><span>Teszt</span></li>',
json[i].size_x,
json[i].size_y,
json[i].col,
json[i].row
);
count++;
};
UPDATE
I deleted the demo, because it's not public anymore. I uploaded my solution. With the table-cell display it didn't work, but if you somehow query the current grid's size-y, you can divide that by 2 and multiply with a little less than a standard grid's size is, depends on the font size you'll use inside the box.
vertical-align only works with table cell. Change the display of gridster to table-cell
.gridster {
display: table-cell;
}
.gridster .gs_w > span {
text-align: center;
vertical-align: middle;
}
I found the solution:
<div style="margin-top:'+(json[i].size_y/2)*130+'px; text-align:center; height:50%">Teszt</div>
Not perfect, I should work out an algorithm instead of y/2*static number, but at least it's working now.
Firstly I tried with span, then with table-cell display, neither of them worked, then I tried with div, didn't work either, but I thought I need a height and a width, so in the ajax call I used the current item's gridster size and used a little hack. Now it's working! :)
$.getJSON( "data/tiles.json", function( json ) {
for(i=0; i<json.length; i++) {
gridster.add_widget(
'<li class="gs_w" id="'+count+'" style="background-color: '+json[i].rgb+'"><div style="margin-top:'+(json[i].size_y/2)*130+'px; text-align:center; height:50%">Teszt</div></li>',
json[i].size_x,
json[i].size_y,
json[i].col,
json[i].row
);
count++;
};
I re-size the grids with select menu, so I could access the size_y that the re-sized grid will use and I changed here the code, so now when you re-size, the text will remain in the center in every situation. :)
...
if ( selected === 'two-wide' ){
x = 2;
y = 1;
}
...
$('#'+widgetId+' div').css({
height: '50%',
'text-align': 'center',
'margin-top': ((y/2)*120)+'px'
});
// resize the current tile
gridster.resize_widget( $('#'+widgetId), x, y, false );

Force divs within table columns to have the same height as column with CSS

Here's my example.
If you have two table columns, one with taller content than the other, then divs inside the other column don't expand to the first column's, even with height:100%. How can I create this behavior with CSS?
<table>
<tr>
<td class="one">
<ul>
<li>fasdf</li>
<li>asgasdf</li>
<li>afdsaggrea</li>
<li>asgasdf</li>
<li>afdsaggrea</li>
</ul>
</td>
<td class="two">
<div class="three">
First div
<div>
Second div
</div>
</div>
</td>
</tr>
</table>
i.e. in my jsfiddle, the behavior I want is the green box filling the entire yellow box.
The reason your div wont grow when you assign it a height of 100% is that the parent doesn't have a height.
ie. You have a list that is causing the table to grow larger.
To remedy this, you can add a height to the parent cell and give the child a height of 100% as shown in this fiddle
.two{
background-color:yellow;
height:100px;
}
.three{
background-color:green;
height:100%;
}
You can add oveflow auto as well.
.three {
height: 100%;
overflow: auto;
}
It will work in some smart browsers like chrome. But it will be an issue in others because the parent doesnt have a known height at the time of rendering.
You can set height to your .two class and then height and overflow properties in .three class should fix your problem.
Or use javascript :)
If you don't want to have to set a concrete height, and you want it to work in all browsers, I added a nicely coded javascript solution to your forked JSFiddle.
var tableCritter = new function TableCritter(){
var critter=this;
var $window = $(window);
var $two = critter.$two = $('.two');
var $three = critter.$three = $('.three');
function adjustHeight(){
$three.css({ height:$two.height() });
};
adjustHeight();
$window.on('resize',adjustHeight);
critter.stop = function(){
$window.off('resize',adjustHeight);
};
};

Overflowing on Firefox and IE9+

I have a red div at a fixed position : top and bottom are fixed.
This div contains a table in which a cell contains a scrollable div, so that the content can't overflow the red div.
It looks like this on Chrome :
But on Firefox, the content grows and overflows out of the red box :
Here's my HTML :
<div id=a>
<div id=b>
<table id=c border=1>
<tr><th height=20 width=20>
<input type=button id=but value="Fill cell">
</th><th>A</th></tr>
<tr><th>dataname</th>
<td><div id=val class=scrollable>cell content</div></td>
</tr>
</table>
</div>
</div>
and the CSS :
#a {
position:fixed; top:20px; left:20px; bottom:50px;width:200px;
background:red;
}
#b { height:100%; }
#c { width:100%; height:100%; }
.scrollable {
width:100%; height:100%;
overflow-y:auto;
}
And here's a fiddle for the tests : http://jsfiddle.net/xusqc/
I'd suggest you test your proposal on FF before submitting an answer.
How could I fix my code to have on Firefox and IE9+ the behavior I have now on Chrome ?
Note that the height of the first line of the table may change dynamically in my application. And the solution must be applicable if my data table has more rows and cells.
As I couldn't find a cross-browser pure HTML/CSS solution, I had to use some JavaScript (and jQuery but it could have been done without it if I hadn't many event handlers bound with jQuery in my cells).
The idea is to empty the cells containing a .scrollable div, compute their heights, refill the cells then set the heights as fixed.
Here's the function I wrote :
function resetScrollableHeights(){
$sp = $('.scrollable').closest('td'), heights = [], contents = [];
$sp.each(function(){
$(this).css('height', 'auto'); // without this, window shrinking would be badly handled
contents.push($('.scrollable', this).detach()); // let's empty all scrollables
});
$sp.each(function(){ //now store the heights
heights.push($(this).height());
});
$sp.each(function(){ // refill the scrollables and fix the heights
$(this).append(contents.shift()).css('height', heights.shift());
});
}
The function is called when the window is resized :
$(window).resize(resetScrollableHeights);
And it must also be called when the content of a .scrollable div changes.
I tested it on Chromium/Ubuntu, Chrome/Android, Firefox/Ubuntu and IE9/Win7. As it's useless on WebKit based browsers where I hadn't any bug with pure HTML/CSS it could be deactivated with some detection but I prefer my code to be free from browser detection.
Demonstration
Change the <td> to <td class="scrollwrap">, the one that contains your target div.
Update your CSS:
:
.scrollwrap { position:relative; }
.scrollable {
position:absolute; width:100%; height:100%; top:0; left:0; right:0; bottom:0;
overflow:auto;
}
But be aware:
height:100% only works when parents also have a specific height, doesn't always work with calculated heights;
when using left&right or top&bottom on position:absolute elements, some browsers can't calculate width & height
overflow-y or overflow-x have some usage limitations.

Resources