I have a table that is populated from a database with a dropdown menu that is populated along side the table which is generated from code in the php. Im tying to use javascript and css to make each drop down open on click but I cannot seem to get it to work. I've included the code below. I initilly had the script working but it only worked on the first instance of the dropdown. I reviewed a similar post to what Im looking to do but it was to no avail.
CSS
background-color: #04AA6D;
color: white;
padding: 5px;
font-size: 12px;
border: none;
text-transform: uppercase;
}
.yardDropbtn i{
font-size: 12px;
top: 1px;
position: relative;
}
.yardDropdown{
position: relative;
display: inline-block;
}
.yard-dropdown-content{
display: none;
position: absolute;
background-color: #f1f1f1;
min-width: 160px;
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
z-index: 1;
right: 0;
}
.yard-dropdown-content a{
color: black;
padding: 5px 16px;
text-decoration: none;
display: block;
}
.yard-dropdown-content a:hover {
background-color: #ddd;
}
.yardDropBtnCt {
display: none;
background-color: #f1f1f1;
color: black;
/*margin-left: 5px;*/
border: 1px;
display: block;
width: 100%;
height: 30px;
font-size: 15px;
text-transform: uppercase;
font-size: 12px;
}
.yardDropBtnCt:hover {
background-color: #ddd;
}
.trackBtn{
display: none;
background-color: #f1f1f1;
color: black;
margin-left: 5px;
border: 1px;
display: block;
width: 100%;
height: 30px;
font-size: 15px;
}
.trackBtn:hover{
background-color: #ddd;
}
/*.yard-dropdown-content:hover{
background-color: #ddd;
}*/
.show_yard_Dropdown .yard-dropdown-content {
display: block;
}
.show_yard_Dropdown .yardDropbtn {
background-color: #3e8e41;
}
JavaScript
var dropDownDivY = document.querySelector(".yardDropdown");
/*dropDownButton.addEventListener("click", function(){
dropDownDivY.classList.toggle('show_yard_Dropdown');
});*/
dropDownButton.forEach(btn => {
btn.onclick = function() {
dropDownDivY.style.display = "block";
}
});
HTML
<tr>
<td width='50px' class='yard8'>6400'</td>
<td width='100px' class='yard8 track'>Receiving 1</td>
<td width='150px' class='yard8'></td>
<td width='150px' class='yard8'>
<td width='250px' class='yard8 destination'></td>
<td width='100px'class='yard8 length'>3455'</td>
<td width='100px' class='yard8 weight'></td>
<td width='150px' class='yard8 status'></td>
<td class='yard8'>
<div class='progressContainer'>
<div class='progress' style='width:54%'>54%</div>
</div>
</td>
<td class='yard8 remarks'></td>
<td width='59px'>
<div class="yardDropdown">
<button class="yardDropbtn">Menu<i class="bx bx-menu"></i></button>
<div class="yard-dropdown-content">Edit TrackAEI Populate</div>
</div>
</td>
</tr>
<form action="" method="POST"></form>
document.querySelector(".yardDropdown");
The querySelector method only returns the first element matching the selector, so it can never work for more than the first row.
You need to use querySelectorAll instead and then loop over all the elements found:
let dropDowns = document.querySelectorAll(".yardDropdown");
for (let i = 0; i < dropDowns.length; i++) {
let dropDown = dropDowns[i]
dropDown.addEventListener('click', function () {
// do something, you can use the `i` variable to know which row has been clicked if you need to
})
}
Related
https://jsfiddle.net/CodeRomeos/402oLqrf/
I want to use the code below that works perfectly for copying text within a . I want to change it to work on a two column table and only copy column 2.
Any help would be appreciated. The working example is in the top link.
<div class="container">
<div class='copied'></div>
<div class="copy-to-clipboard">
<input readonly type="text" value="Click Me To Copy" readonly>
</div>
</div>
$(function() {
$('.copy-to-clipboard input').click(function() {
$(this).focus();
$(this).select();
document.execCommand('copy');
$(".copied").text("Copied to clipboard").show().fadeOut(2500);
});
});
.container {
max-width: 25rem;
background: #f7f7f7;
margin: 2rem auto;
position: relative;
padding: 2rem;
.copy-to-clipboard input {
border: none;
background: #fff;
padding: 0.5rem;
width: 100%;
cursor: pointer;
box-sizing: border-box;
}
.copied {
position: absolute;
background: #6ce890;
color: #000;
padding: 0.2rem;
font-weight: bold;
z-index: 99;
bottom: 0;
text-align: center;
display: none;
font-size: .8rem;
}
}
The code in the example above is what I want to use but instead of all the text in a <div> only make column 2 able to be copied.
So say the first row of a 2 column table has the following:
Column1 Column2
Sometext column 1 Some text column 2
only copy Some text in Column 2 to the clipboard.
Assuming you'll use a table structure, you can find the input in the adjacent cell with $(this).closest('td').next('td').find('input')
$(function() {
$('.copy-cell').click(function() {
const other = $(this).next('td')
other.find('input').focus();
other.find('input').select();
document.execCommand('copy');
$(".copied").text("Copied to clipboard").show().fadeOut(2500);
});
});
.container td {
max-width: 25rem;
background: #f7f7f7;
margin: 2rem auto;
position: relative;
padding: 2rem;
}
td.copy-cell {
cursor: pointer;
}
input {
border: none;
background: #fff;
padding: 0.5rem;
width: 100%;
box-sizing: border-box;
}
.copied {
position: absolute;
background: #6ce890;
color: #000;
padding: 0.2rem;
font-weight: bold;
z-index: 99;
bottom: 0;
text-align: center;
display: none;
font-size: .8rem;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table class="container">
<tr>
<td class='copy-cell'>
Click on this
</td>
<td>
<input readonly type="text" value="... to copy this" readonly>
<div class='copied'></div>
</td>
</tr>
</table>
Hello, this is the link before click. And when I click on that link, that link go to upper side of the page, like this;
You can see the difference. I am also attaching the code please check.
Here is what I am working on;
function myFunction() {
document.getElementById("myDropdown").classList.toggle("show");
}
// Close the dropdown if the user clicks outside of it
window.onclick = function(e) {
if (!e.target.matches('.dropbtn')) {
var myDropdown = document.getElementById("myDropdown");
if (myDropdown.classList.contains('show')) {
myDropdown.classList.remove('show');
}
}
}
.dropdown-content {
display: none;
position: relative;
width: 200px;
background-color: #FFF;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
z-index: 1;
}
.dropdown-content a {
float: none;
color: #000;
text-decoration: none;
display: block;
padding: 5px 10px;
font-size: 13px;
text-align: left;
}
.dropdown-content a:hover {
background-color: #ddd;
}
.show {display: block;}
<div class="header">
<table border="0" width="100%">
<tr>
<td width="5%" valign="top"><img src="./images/logo_small.png" alt="" title=""></td>
<td width="45%"><div class="web_title">ASHNAB</div></td>
<td width="50%" align="right">
<div class="header_links">
Link Drop Down
</div>
<div id="myDropdown" class="dropdown-content">
Home
About
Contact
</div>
</td>
</tr>
</table>
</div>
I don't know where the issue lies.
Please help
It seems like a small thing, but there you go. Change only the CSS:
.dropdown-content {
display: none;
position: absolute;
right: 0;
top: 2rem;
width: 200px;
background-color: #FFF;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
z-index: 1;
}
The absolute positioning takes the element out of the normal flow. That's about it.
With two columns (left indicates name; right provide collapse function(a button)), how would you create collapsible content on the next row with full width (covers both columns)?
I am only able to collapse within a certain column. I tried to collapse a row below by creating a new div, but then the collapsing action no longer seems to work.
It should look like this:
Thank you for your help!
JavaScript is from: https://www.w3schools.com/howto/howto_js_collapsible.asp
var coll = document.getElementsByClassName("collapsible");
var i;
for (i = 0; i < coll.length; i++) {
coll[i].addEventListener("click", function() {
this.classList.toggle("active");
var content = this.nextElementSibling;
if (content.style.maxHeight){
content.style.maxHeight = null;
} else {
content.style.maxHeight = content.scrollHeight + "px";
}
});
}
:root {
--colorbggray: rgb(65,65,65);
--colorlightgray: rgb(150,150,150);
--colorcyan: rgb(0, 229, 255);
--colorgreen: rgb(0, 255, 0);
--colorUpGrey: rgb(135,135,135);
--colorLowGrey: rgb(38,38,38);
--colorMidGrey: rgb(95,95,95);
--colorGreen: rgb(11,69,2);
--colorAmber: orange;
--colorRed: red;
}
.verticalmenu-auto {
display: flex;
align-items: stretch;
background-color: #f1f1f1;
}
.verticalmenu-auto > div {
border-top: 2px solid var(--colorUpGrey);
border-bottom: 2px solid var(--colorLowGrey);
border-left: 2px solid var(--colorUpGrey);
border-right: 2px solid var(--colorLowGrey);
background: var(--colorMidGrey); color: white;
width: 100px;
padding-top: 8px;
padding-bottom: 8px;
padding-right: 16px;
padding-left: 16px;
margin: 0.5px;
text-align: left;
font-size: 16px;
}
.verticalmenu-auto li, {
border-top: 2px solidauto var(--colorUpGrey);
border-bottom: 2px solid var(--colorLowGrey);
border-left: 2px solid var(--colorUpGrey);
border-right: 2px solid var(--colorLowGrey);
background: var(--colorMidGrey);
}
.collapsible {
background: var(--colorMidGrey); color: white;
cursor: pointer;
width: 100%;
border: none;
text-align: center;
outline: none;
}
.collapsible:after {
content: '\1433';
float: center;
transform: scale(.7, 1);
}
.active:after {
content: "\142F";
transform: scale(1, .7);
}
.content {
max-height: 0;
overflow: hidden;
transition: max-height 0.0s ease-out;
grid-column-start: -1;
grid-column-end: 1;
background-color: gray;
}
<body>
<p>On right side open content which is has the width of both columns combined</p>
<ul style="list-style:none;padding-left:0;">
<li>
<div class="verticalmenu-auto">
<div style="flex-grow: 10">Name</div>
<div style="flex-grow: 1; text-align: center">
<button class="collapsible"></button>
<div class='content'>
<p> content</p>
</div>
</div>
</div>
</li>
<li>
<div class="verticalmenu-auto">
<div style="flex-grow: 10">Name</div>
<div style="flex-grow: 1; text-align: center">
<button class="collapsible"></button>
<div class='content'>
<p> content</p>
</div>
</div>
</div>
</li>
A slight change in your JS toggle and also putting content outside the parent div so that it act as block
$(document).ready(function () {
$('.collapsible').on('click', function(event){
event.preventDefault();
// create accordion variables
var accordion = $(this);
var accordionContent = accordion.closest('.verticalmenu-auto').next('.content');
// toggle accordion link open class
accordion.toggleClass("active");
// toggle accordion content
accordionContent.slideToggle(250);
accordionContent.toggleClass("active");
});
});
:root {
--colorbggray: rgb(65,65,65);
--colorlightgray: rgb(150,150,150);
--colorcyan: rgb(0, 229, 255);
--colorgreen: rgb(0, 255, 0);
--colorUpGrey: rgb(135,135,135);
--colorLowGrey: rgb(38,38,38);
--colorMidGrey: rgb(95,95,95);
--colorGreen: rgb(11,69,2);
--colorAmber: orange;
--colorRed: red;
}
.verticalmenu-auto {
display: flex;
align-items: stretch;
background-color: #f1f1f1;
}
.verticalmenu-auto > div {
border-top: 2px solid var(--colorUpGrey);
border-bottom: 2px solid var(--colorLowGrey);
border-left: 2px solid var(--colorUpGrey);
border-right: 2px solid var(--colorLowGrey);
background: var(--colorMidGrey); color: white;
width: 100px;
padding-top: 8px;
padding-bottom: 8px;
padding-right: 16px;
padding-left: 16px;
margin: 0.5px;
text-align: left;
font-size: 16px;
}
.verticalmenu-auto li, {
border-top: 2px solidauto var(--colorUpGrey);
border-bottom: 2px solid var(--colorLowGrey);
border-left: 2px solid var(--colorUpGrey);
border-right: 2px solid var(--colorLowGrey);
background: var(--colorMidGrey);
}
.collapsible {
background: var(--colorMidGrey); color: white;
cursor: pointer;
width: 100%;
border: none;
text-align: center;
outline: none;
}
.collapsible:after {
content: '\1433';
float: center;
transform: scale(.7, 1);
}
.collapsible.active:after {
content: "\142F";
transform: scale(1, .7);
}
.content {
display: none;
overflow: hidden;
transition: max-height 0.0s ease-out;
grid-column-start: -1;
grid-column-end: 1;
background-color: #BFBFBF;
padding: 10px;
color: #fff;
}
.content.active {
height: auto;
display: block !important;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<body>
<p>On right side open content which is has the width of both columns combined</p>
<ul style="list-style:none;padding-left:0;">
<li>
<div class="verticalmenu-auto">
<div style="flex-grow: 10">Name</div>
<div style="flex-grow: 1; text-align: center">
<button class="collapsible"></button>
</div>
</div>
<div class='content'>
<p> content</p>
</div>
</li>
<li>
<div class="verticalmenu-auto">
<div style="flex-grow: 10">Name</div>
<div style="flex-grow: 1; text-align: center">
<button class="collapsible"></button>
</div>
</div>
<div class='content'>
<p> content</p>
</div>
</li>
Are you need like this?
var buttons = document.querySelectorAll('.toggle');
buttons.forEach(function(el) {
el.addEventListener('click', function(event) {
var target = event.target.getAttribute('target');
document.getElementById(target).classList.toggle('expanded');
});
});
table {
width: 100%;
}
table td:nth-child(2) {
width: 100px;
}
.row-detail {
display: none;
}
.expanded {
display: block;
}
<table>
<tr>
<td>Row 1</td>
<td><button class="toggle" target="row-detail-1">Toggle 1</button></td>
</tr>
<tr id="row-detail-1" class="row-detail">
<td colspan="2">This detail from row 1</td>
</tr>
<tr>
<td>Row 2</td>
<td><button class="toggle" target="row-detail-2">Toggle 2</button></td>
</tr>
<tr id="row-detail-2" class="row-detail">
<td colspan="2">This detail from row 2</td>
</tr>
<tr>
<td>Row 3</td>
<td><button class="toggle" target="row-detail-3">Toggle 3</button></td>
</tr>
<tr id="row-detail-3" class="row-detail">
<td colspan="2">This detail from row 3</td>
</tr>
</table>
I want to show changes made to the value in any cell in a one row table from a drop down list to be in red. Each cell has an associated dropdown. However if the same value in the dropdown and the cell is clicked the value in the cell shouldn’t change color. I thought I could keep a cloned copy of the original Table cell data in a separate function - then compare any changes if they are made. However this information is deleted each time in the function shown here as a user goes from cell to cell. The table consists of a series of cells - one cell is included here.
// Attach listeners
window.onload = function() {
var cellData = document.querySelectorAll('.dropdown-content p').forEach(
node => node.addEventListener('click', displayCellData, false)
);
}
function displayCellData(evt) {
// to display the previous cell data
evt.stopPropagation();
var cell = this.closest('td');
var origCellVal = [];
// Array of original cell values
var previous = cell.previousElementSibling.innerHTML;
console.log(previous);
var cellVal = $(evt.target).text();
origCellVal[cell.cellIndex] = previous;
document.getElementById("displayArray").innerHTML = origCellVal;
if (cellVal != previous) {
previous = cellVal;
// only change the text color if cellVal changed
cell.previousElementSibling.innerHTML = "<span class='color-red'>" +
cellVal + "</span>";
if (previous = origCellVal[cell.cellIndex]) {
cellVal.innerHTML = "<span class='color-black'>" + cellVal +
"</span>";
}
}
}
table#t00, th,td {
border: 1px solid red;
width:80%;
margin-left:15%;
margin-right:15%;
}
table#t01 {
table-layout: fixed;
width: 100%;
background-color: #f1f1c1;
height: 50px;
text-align: center;
font-size: large;
}
table#t02 {
table-layout: fixed;
width: 100%;
background-color: #f1f1c1;
height: 50px;
text-align: center;
font-size: large;
}
.equal-width td {
width: 5%;
}
.dropbtn {
background-color: #4CAF50;
color: white;
padding: 5px;
font-size: 16px;
border: none;
cursor: pointer;
}
.dropdown {
position: relative;
display: inline-block;
}
.dropdown-content {
display: none;
position: absolute;
background-color: #f9f9f9;
min-width: 100px;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.8);
z-index: 1;
}
.dropdown-content a {
color: black;
padding: 12px 16px;
text-decoration: none;
display: block;
}
.dropdown-content a:hover {background-color: #f1f1f1}
.dropdown:hover .dropdown-content {
display: block;
}
.dropdown:hover .dropbtn {
background-color: #3e8e41;
}
.color-red{
color: #F00;
}
.color-black{
color: #000;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p id="displayArray"></p>
<table>
<tr>
<td>Closed</td>
<td>
<div class="dropdown">
<button class="dropbtn">Change?</button>
<div class="dropdown-content">
<p>OPEN</p>
<p>Closed</p>
</div>
</div>
</td>
</td>
. .
</tr>
</table>
You're creating a new, empty origCellValue array every time you call displayCellData(), so it doesn't hold the values from previous calls.
However, rather than using a separate array, I suggest you save the information in the DOM itself. You can use a dataset attribute on the element to save the information.
And rather than inserting new HTML with <span> elements, you can simply change the class list of the <td> itself.
cellVal.innerHTML is just wrong. cellVal is a string, not a DOM element.
// Attach listeners
window.onload = function() {
var cellData = document.querySelectorAll('.dropdown-content p').forEach(
node => node.addEventListener('click', displayCellData, false)
);
}
function displayCellData(evt) {
// to display the previous cell data
evt.stopPropagation();
var cell = this.closest('td');
var prev = cell.previousElementSibling;
if (!prev.dataset.origVal) {
prev.dataset.origVal = prev.innerText;
}
var origVal = prev.dataset.origVal;
if (evt.target.textContent == origVal) {
prev.classList.remove("color-red");
prev.classList.add("color-black");
} else {
prev.classList.remove("color-black");
prev.classList.add("color-red");
}
prev.textContent = evt.target.textContent;
}
table#t00,
th,
td {
border: 1px solid red;
width: 80%;
margin-left: 15%;
margin-right: 15%;
}
table#t01 {
table-layout: fixed;
width: 100%;
background-color: #f1f1c1;
height: 50px;
text-align: center;
font-size: large;
}
table#t02 {
table-layout: fixed;
width: 100%;
background-color: #f1f1c1;
height: 50px;
text-align: center;
font-size: large;
}
.equal-width td {
width: 5%;
}
.dropbtn {
background-color: #4CAF50;
color: white;
padding: 5px;
font-size: 16px;
border: none;
cursor: pointer;
}
.dropdown {
position: relative;
display: inline-block;
}
.dropdown-content {
display: none;
position: absolute;
background-color: #f9f9f9;
min-width: 100px;
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.8);
z-index: 1;
}
.dropdown-content a {
color: black;
padding: 12px 16px;
text-decoration: none;
display: block;
}
.dropdown-content a:hover {
background-color: #f1f1f1
}
.dropdown:hover .dropdown-content {
display: block;
}
.dropdown:hover .dropbtn {
background-color: #3e8e41;
}
.color-red {
color: #F00;
}
.color-black {
color: #000;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p id="displayArray"></p>
<table>
<tr>
<td>Closed</td>
<td>
<div class="dropdown">
<button class="dropbtn">Change?</button>
<div class="dropdown-content">
<p>OPEN</p>
<p>Closed</p>
</div>
</div>
</td>
</td>
. .
</tr>
</table>
I want to create PDF dynamically. That means I am taking files from Google Drive and then putting them in a HTML code and trying to create PDF out of it.
Everything is working fine , except images are not showing up.
What I am doing right now is :
Create HtmlOutput from HTML string.
Getting blob of that HTML.
Then that blob getAs PDF
Then finally saving that PDF in drive.
I thought maybe it need some time to render images so I have added sleep.
Alternative suggestions/solutions/code is also welcomed.
Here is the code I am using :
function htmlToPDF() {
var file1="0BweO_SXcQGqgcEV3Mk9QYVRMczQ";
var file2="0BweO_SXcQGqgVU93ZU56WkRkN3c";
var file3="0BweO_SXcQGqgMkIxTlhKajk5MFk";
var html = "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'> <html xmlns='http://www.w3.org/1999/xhtml'> <head> <meta http-equiv='Content-Type' content='text/html; charset=utf-8' /> <title>High Return Real Estate</title> <style> #charset 'utf-8'; /* CSS Document */ body,html{ font-family: 'Lato', sans-serif; background:#dfe7ea; margin-top: 0; margin-bottom:0; } .main_wrapper { width: 930px; margin: 0 auto; background: #fff; padding: 0 23px 0 23px; box-shadow: 1px 1px 8px -1px #bbbbbb; } .top_header { background: #dfe9e9; display: inline-block; width: 100%; margin-bottom: 6px; } .left_top { display: inline-block; width: 70%; padding: 26px 0 26px 50px; float: left; } p.contact_person.right_person { border-right: 0; padding-left: 25px; display: inline-block; } p.contact_person { width: 214px; display: inline-block; text-align: left; border-right: 1px solid #ccd4d3; } .left_top p span { font-size: 13px; color: #000; display: inline-block; width: 100%; margin-top: 2px; } .left_top p { font-size: 20px; font-weight: bold; color: #ea9a5a; margin: 0; } .right_top img { border-top: 7px solid #496bb3; border-bottom: 7px solid #496bb3; padding: 16px 5px 13px 4px; } .right_top { display: inline-block; position: absolute; background: #fff; } .galleryimage_main img { width: 100%; display: inline-block; } .property_address { } .property_address p { font-size: 15px; text-transform: uppercase; color: #fff; margin-bottom: 0; margin-top: 4px; } .common_box_style { padding: 20px; background: #496bb3; text-transform: uppercase; } .common_box_style h2 { margin-top: 2px; color: #54bb73; font-size: 20px; } .property_information { margin-left: 6px; } .property_information ul li p { font-size: 15px; color: #fff; text-transform: capitalize; margin-bottom: 3px; margin-top: 0; } .property_information ul li { list-style: none; margin-bottom: 5px; } .property_information ul li span { font-size: 15px; color: #fff; text-transform: capitalize; margin-bottom: 3px; margin-top: 0; width: 100px; display: inline-block; text-align: left; } .property_information ul { padding-left: 0; width: auto; display: inline-block; float: left; margin-right: 15px; margin-top: 0; margin-bottom: 4px; } .property_information ul li span.value_field { font-size: 13px; } ul.grpleft li span { width: 136px; } p.cash_prize { text-align: center; font-size: 18px; font-weight: bold; color: #fff; display: inline-block; width: 100%; margin: 0; } p.cash_prize span { padding-left: 10px; } .margintop{ margin-top: 6px; } .left_col { width: 34%; float: left; } .right_col { display: inline-block; width: 66%; } .gallery_small_images { display: inline-block; width: 99%; margin-left: 6px; margin-top: 6px; height: 232px; overflow: hidden; } .gallery_small_images img{ width:100%; } .gallery_small_image2 { width: 50%; float: left; } .gallery_small_image3 { width: 49%; display: inline-block; float: left; margin-left: 6px; } table.expense_info tr td.exp_info_disc { font-size: 14px; color: #fff; text-transform: capitalize; text-align: left; } tr.total_exp td { padding-top: 13px; padding-bottom: 30px; } .cashflow h2 { margin-bottom: 7px; } .roi input[type='text'] { font-size: 23px; text-align: center; font-weight: bold; height: 33px; margin-bottom: 5px; } .roi h2 { margin-top: 24px; font-size: 22px; font-weight: 800; } tr.total_exp { font-weight: bold; margin-top: 4px; /* display: table; */ width: 100%; } table.expense_info th { font-size: 12px; font-weight: bold; color: #fff; text-transform: capitalize; } td { font-size: 13px; color: #fff; text-align: center; vertical-align: middle; } .expense_info.common_box_style.margintop { padding: 20px 13px; } table.cashflow { text-align: center; margin: 0 auto; color: #fff; text-transform: capitalize; } table.cashflow tr th { font-size: 19px; font-weight: normal; } table.cashflow tr td { font-size: 17px; font-weight: bold; } .roi { display: inline-block; background: #2bb673; width: 99%; text-align: center; color: #000; padding: 10px 0px 20px 0; margin-left: 6px; margin-top: 6px; } .discliamer_sec { background: #dfe9e9; display: inline-block; text-align: center; font-size: 9px; width: 100%; padding: 10px 0; line-height: 18px; margin-top: 6px; } .discliamer_sec h2 { color: #768293; } .footer_cright { background: #496bb3; text-align: center; font-size: 8px; color: #fff; padding: 1px 0; font-weight: 400; margin-top: 7px; display: inline-block; width: 100%; } #media print { .common_box_style, .roi, .discliamer_sec, .footer_cright, .top_header { background: !important; -webkit-print-color-adjust: exact; } } </style> <link href='https://fonts.googleapis.com/css?family=Lato:400,700' rel='stylesheet'> </head> <body> <div class='main_wrapper'> <div class='top_header'> <div class='left_top'><p class='contact_person'>Xyz xyz<span>xyz#xyz.com</span></p><p class='contact_person right_person'>Xyz xyz<span>xyz#xyz.com</span></p></div> <div class='right_top'>";
html +="<img src='images/logo.png' /></div> </div> <div class='galleryimage_main'> <img src='https://drive.google.com/uc?export=view&id="+file1+"' alt='Gallery Image Here' height='350' width='885'/> </div> <div class='left_col'> <div class='property_address common_box_style margintop'> <h2>PROPERTY ADDRESS</h2> <p id='address_line_1' class='address_line_1'>4043 N RAVENSWOOD AVE,</p> <p id='address_line_2' class='address_line_2'>CSUITE 316, HICAGO,</p> </div> <div class='expense_info common_box_style margintop'> <h2>EXPENSE INFORMATION</h2> <table id='expense_information' class='expense_info' cellpadding='3' cellspacing='1'> <tr> <th></th> <th>%of Rent</th> <th>Monthly</th> <th>Yearly</th> </tr> <tr> <td class='exp_info_disc'>Estimated Taxes:</td> <td>-</td> <td>345</td> <td>777</td> </tr> <td class='exp_info_disc'>Estimated Insurance:</td> <td>-</td> <td>200</td> <td>133</td> </tr> <tr> <td class='exp_info_disc'>Management Fee:</td> <td>4</td> <td>344</td> <td>5200</td> </tr> <tr> <td class='exp_info_disc'>Vacancy Adjustment:</td> <td>2</td> <td>555</td> <td>50</td> </tr> <tr> <td class='exp_info_disc'>Repairs & Maintenance: </td> <td>10</td> <td>500</td> <td>50</td> </tr> <tr> </tr> <tr> </tr> <tr> </tr> <tr is='total_exp' class='total_exp'> <td class='exp_info_disc'>TOTAL EXPENSES:</td> <td></td> <td>343</td> <td>342</td> </tr> <tr> </tr><tr> </tr><tr> </tr> </table> </div> <div class='cashflow common_box_style margintop'> <h2>CASH FLOW ESTIMATE</h2> <table id='cashflow' class='cashflow' cellpadding='3' cellspacing='5'> <tr> <th>Per Month</th> <th>Per Year</th> </tr> <tr> <td>123</td> <td>4533</td> </tr> </table> </div> </div> <div class='right_col'> <div id='property_information' class='property_information common_box_style margintop'> <h2>PROPERTY Information</h2> <ul> <li><span>List Price:</span><span class='value_field'>100</span></p></li> <li><span>Rehab Costs:</span><span class='value_field'>100</span></p></li> <li><span>Structure:</span><span class='value_field'>Single Family</span></li> <li><span>Current Rent:</span><span class='value_field'>29</span></li> </ul> <ul class='grpleft'> <li><span>Bedrooms:</span><span class='value_field'>100</span></li> <li><span>Bathrooms:</span><span class='value_field'>100</span></p></li> <li><span>Square Footage:</span><span class='value_field'>100</span></p></li> <li><span> Current Status:</span><span class='value_field'>Rented and Performing </span></li> </ul> <p class='cash_prize'>CASH PRICE<span>300</span></p> </div> <div class='gallery_small_images'> <div class='gallery_small_image2'> <img src='https://drive.google.com/uc?export=view&id="+file2+"' /> </div> <div class='gallery_small_image3'> <img src='https://drive.google.com/uc?export=view&id="+file3+"' /> </div></div> <div class='roi' id='cash_on_cash'> <h2>CASH-ON-CASH ROI</h2> <input type='text' value='1200' id='cash_roi_input'></input> </div> </div> <div class='discliamer_sec'> <h2>Information deemed reliabale but not guranteed. Images not to scale. Buyer responsible for <br>due diligence for all information provided and should conduct their own research.</h2> </div> <div class='footer_cright'> <h2>Copyright © High Return Real Estate LLC - 2017</h2> </div> </div> </body> </html>";
var blob = HtmlService.createHtmlOutput(html);
//Utilities.newBlob(html, "text/html", "text.html");
Utilities.sleep(30000);
blob = blob.getBlob();
var pdf = blob.getAs("application/pdf");
Utilities.sleep(30000);
DriveApp.createFile(pdf).setName("text.pdf");
}
You can download the image file using URLfetchApp and convert it into a base64 data string and create a page with that. The remaining procedure is same as previous
function htmlToPDF() {
var file = []
file[0]="0BweO_SXcQGqgcEV3Mk9QYVRMczQ"
file[1]="0BweO_SXcQGqgVU93ZU56WkRkN3c"
file[2]="0BweO_SXcQGqgMkIxTlhKajk5MFk"
var url = "https://drive.google.com/uc?export=view&id="
var img = UrlFetchApp.fetch(url+file[0])
file[0] = img.getBlob().getContentType()+';base64,'+ Utilities.base64Encode(img.getBlob().getBytes())
var img = UrlFetchApp.fetch(url+file[1])
file[1] = img.getBlob().getContentType()+';base64,'+ Utilities.base64Encode(img.getBlob().getBytes())
var img = UrlFetchApp.fetch(url+file[2])
file[2] = img.getBlob().getContentType()+';base64,'+ Utilities.base64Encode(img.getBlob().getBytes())
var html = "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'> <html xmlns='http://www.w3.org/1999/xhtml'> <head> <meta http-equiv='Content-Type' content='text/html; charset=utf-8' /> <title>High Return Real Estate</title> <style> #charset 'utf-8'; /* CSS Document */ body,html{ font-family: 'Lato', sans-serif; background:#dfe7ea; margin-top: 0; margin-bottom:0; } .main_wrapper { width: 930px; margin: 0 auto; background: #fff; padding: 0 23px 0 23px; box-shadow: 1px 1px 8px -1px #bbbbbb; } .top_header { background: #dfe9e9; display: inline-block; width: 100%; margin-bottom: 6px; } .left_top { display: inline-block; width: 70%; padding: 26px 0 26px 50px; float: left; } p.contact_person.right_person { border-right: 0; padding-left: 25px; display: inline-block; } p.contact_person { width: 214px; display: inline-block; text-align: left; border-right: 1px solid #ccd4d3; } .left_top p span { font-size: 13px; color: #000; display: inline-block; width: 100%; margin-top: 2px; } .left_top p { font-size: 20px; font-weight: bold; color: #ea9a5a; margin: 0; } .right_top img { border-top: 7px solid #496bb3; border-bottom: 7px solid #496bb3; padding: 16px 5px 13px 4px; } .right_top { display: inline-block; position: absolute; background: #fff; } .galleryimage_main img { width: 100%; display: inline-block; } .property_address { } .property_address p { font-size: 15px; text-transform: uppercase; color: #fff; margin-bottom: 0; margin-top: 4px; } .common_box_style { padding: 20px; background: #496bb3; text-transform: uppercase; } .common_box_style h2 { margin-top: 2px; color: #54bb73; font-size: 20px; } .property_information { margin-left: 6px; } .property_information ul li p { font-size: 15px; color: #fff; text-transform: capitalize; margin-bottom: 3px; margin-top: 0; } .property_information ul li { list-style: none; margin-bottom: 5px; } .property_information ul li span { font-size: 15px; color: #fff; text-transform: capitalize; margin-bottom: 3px; margin-top: 0; width: 100px; display: inline-block; text-align: left; } .property_information ul { padding-left: 0; width: auto; display: inline-block; float: left; margin-right: 15px; margin-top: 0; margin-bottom: 4px; } .property_information ul li span.value_field { font-size: 13px; } ul.grpleft li span { width: 136px; } p.cash_prize { text-align: center; font-size: 18px; font-weight: bold; color: #fff; display: inline-block; width: 100%; margin: 0; } p.cash_prize span { padding-left: 10px; } .margintop{ margin-top: 6px; } .left_col { width: 34%; float: left; } .right_col { display: inline-block; width: 66%; } .gallery_small_images { display: inline-block; width: 99%; margin-left: 6px; margin-top: 6px; height: 232px; overflow: hidden; } .gallery_small_images img{ width:100%; } .gallery_small_image2 { width: 50%; float: left; } .gallery_small_image3 { width: 49%; display: inline-block; float: left; margin-left: 6px; } table.expense_info tr td.exp_info_disc { font-size: 14px; color: #fff; text-transform: capitalize; text-align: left; } tr.total_exp td { padding-top: 13px; padding-bottom: 30px; } .cashflow h2 { margin-bottom: 7px; } .roi input[type='text'] { font-size: 23px; text-align: center; font-weight: bold; height: 33px; margin-bottom: 5px; } .roi h2 { margin-top: 24px; font-size: 22px; font-weight: 800; } tr.total_exp { font-weight: bold; margin-top: 4px; /* display: table; */ width: 100%; } table.expense_info th { font-size: 12px; font-weight: bold; color: #fff; text-transform: capitalize; } td { font-size: 13px; color: #fff; text-align: center; vertical-align: middle; } .expense_info.common_box_style.margintop { padding: 20px 13px; } table.cashflow { text-align: center; margin: 0 auto; color: #fff; text-transform: capitalize; } table.cashflow tr th { font-size: 19px; font-weight: normal; } table.cashflow tr td { font-size: 17px; font-weight: bold; } .roi { display: inline-block; background: #2bb673; width: 99%; text-align: center; color: #000; padding: 10px 0px 20px 0; margin-left: 6px; margin-top: 6px; } .discliamer_sec { background: #dfe9e9; display: inline-block; text-align: center; font-size: 9px; width: 100%; padding: 10px 0; line-height: 18px; margin-top: 6px; } .discliamer_sec h2 { color: #768293; } .footer_cright { background: #496bb3; text-align: center; font-size: 8px; color: #fff; padding: 1px 0; font-weight: 400; margin-top: 7px; display: inline-block; width: 100%; } #media print { .common_box_style, .roi, .discliamer_sec, .footer_cright, .top_header { background: !important; -webkit-print-color-adjust: exact; } } </style> <link href='https://fonts.googleapis.com/css?family=Lato:400,700' rel='stylesheet'> </head> <body> <div class='main_wrapper'> <div class='top_header'> <div class='left_top'><p class='contact_person'>Xyz xyz<span>xyz#xyz.com</span></p><p class='contact_person right_person'>Xyz xyz<span>xyz#xyz.com</span></p></div> <div class='right_top'>";
html +="<img src='images/logo.png' /></div> </div> <div class='galleryimage_main'> <img src='data:"+file[0]+"' alt='Gallery Image Here' height='350' width='885'/> </div> <div class='left_col'> <div class='property_address common_box_style margintop'> <h2>PROPERTY ADDRESS</h2> <p id='address_line_1' class='address_line_1'>4043 N RAVENSWOOD AVE,</p> <p id='address_line_2' class='address_line_2'>CSUITE 316, HICAGO,</p> </div> <div class='expense_info common_box_style margintop'> <h2>EXPENSE INFORMATION</h2> <table id='expense_information' class='expense_info' cellpadding='3' cellspacing='1'> <tr> <th></th> <th>%of Rent</th> <th>Monthly</th> <th>Yearly</th> </tr> <tr> <td class='exp_info_disc'>Estimated Taxes:</td> <td>-</td> <td>345</td> <td>777</td> </tr> <td class='exp_info_disc'>Estimated Insurance:</td> <td>-</td> <td>200</td> <td>133</td> </tr> <tr> <td class='exp_info_disc'>Management Fee:</td> <td>4</td> <td>344</td> <td>5200</td> </tr> <tr> <td class='exp_info_disc'>Vacancy Adjustment:</td> <td>2</td> <td>555</td> <td>50</td> </tr> <tr> <td class='exp_info_disc'>Repairs & Maintenance: </td> <td>10</td> <td>500</td> <td>50</td> </tr> <tr> </tr> <tr> </tr> <tr> </tr> <tr is='total_exp' class='total_exp'> <td class='exp_info_disc'>TOTAL EXPENSES:</td> <td></td> <td>343</td> <td>342</td> </tr> <tr> </tr><tr> </tr><tr> </tr> </table> </div> <div class='cashflow common_box_style margintop'> <h2>CASH FLOW ESTIMATE</h2> <table id='cashflow' class='cashflow' cellpadding='3' cellspacing='5'> <tr> <th>Per Month</th> <th>Per Year</th> </tr> <tr> <td>123</td> <td>4533</td> </tr> </table> </div> </div> <div class='right_col'> <div id='property_information' class='property_information common_box_style margintop'> <h2>PROPERTY Information</h2> <ul> <li><span>List Price:</span><span class='value_field'>100</span></p></li> <li><span>Rehab Costs:</span><span class='value_field'>100</span></p></li> <li><span>Structure:</span><span class='value_field'>Single Family</span></li> <li><span>Current Rent:</span><span class='value_field'>29</span></li> </ul> <ul class='grpleft'> <li><span>Bedrooms:</span><span class='value_field'>100</span></li> <li><span>Bathrooms:</span><span class='value_field'>100</span></p></li> <li><span>Square Footage:</span><span class='value_field'>100</span></p></li> <li><span> Current Status:</span><span class='value_field'>Rented and Performing </span></li> </ul> <p class='cash_prize'>CASH PRICE<span>300</span></p> </div> <div class='gallery_small_images'> <div class='gallery_small_image2'> <img src='data:"+file[1]+"' /> </div> <div class='gallery_small_image3'> <img src='data:"+file[2]+"' /> </div></div> <div class='roi' id='cash_on_cash'> <h2>CASH-ON-CASH ROI</h2> <input type='text' value='1200' id='cash_roi_input'></input> </div> </div> <div class='discliamer_sec'> <h2>Information deemed reliabale but not guranteed. Images not to scale. Buyer responsible for <br>due diligence for all information provided and should conduct their own research.</h2> </div> <div class='footer_cright'> <h2>Copyright © High Return Real Estate LLC - 2017</h2> </div> </div> </body> </html>";
var blob = HtmlService.createHtmlOutput(html);
//Utilities.newBlob(html, "text/html", "text.html");
blob = blob.getBlob();
var pdf = blob.getAs("application/pdf");
DriveApp.createFile(pdf).setName("text.pdf");
}
/**
* This function retrieve the html body.
* Using regex searches all the images src attribute and create array of image URLs
* Iterate over imageURL & retrieve image blob from URLFetchApp and convert it into base64
* Then replaces the imageURL with base64 with proper formatting
* Then convert the htmlText to PDF and save it in folder
* #param {string} messageID
* #param {string} folderID
* #param {string} fileURL
*/
function convertMailBodyToPDF(messageID, folderID) {
const mail = GmailApp.getMessageById(messageID);
let htmlText = mail.getBody();
const images = getAllImageTags(htmlText);
images.forEach(image => {
const res = UrlFetchApp.fetch(image);
const imgbase64 = "data:" + res.getBlob().getContentType() + ';base64,' + Utilities.base64Encode(res.getBlob().getBytes());
htmlText = replaceAll(htmlText, image, imgbase64);
});
const blob = htmlToPDF(htmlText);
const file = DriveApp.getFolderById(folderID).createFile(blob).setName("testimage.pdf");
console.log(file.getUrl());
return file.getUrl();
}
/**
* This converts html to pdf blob
* #param {string} text
* #param {Blob} blob
*/
function htmlToPDF(text) {
const html = HtmlService.createHtmlOutput(text);
const blob = html.getBlob();
return blob.getAs("application/pdf");
}
/**
* Retrieve all image src
* #param {string} str
* #return {Array} imageURL
*/
function getAllImageTags(str) {
let words = [];
str.replace(/<img[^>]+src="([^">]+)"/g, function ($0, $1) { words.push($1) });
return words;
}
const escapeRegExp = (string) => string.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
const replaceAll = (str, find, replace) => str.replace(new RegExp(escapeRegExp(find), 'g'), replace);
You saved me with this:
> const images = getAllImageTags(htmlText); images.forEach(image => {
> const res = UrlFetchApp.fetch(image);
> const imgbase64 = "data:" + res.getBlob().getContentType() + ';base64,' + Utilities.base64Encode(res.getBlob().getBytes());
> htmlText = replaceAll(htmlText, image, imgbase64); });
I just edited it:
htmlText = htmlText.replace(image, imgbase64);
I needed to Compile a Google Doc Template that I have in DRIVE which is a letterhead with a Header and a Footer which are images and as body there are Placeholders, ex. {{name}} {{surname}}; finally upload to DRIVE as a PDF.
So the steps were:
from DOC to HTML
from HTML to BLOB
from BLOB to PDF
Everything worked correctly, but in the conversion to PDF I was missing the images... which had to be replaced by the same images but converted to Base64 ... and your procedure works great!
Thanks!!!!!!