Can't append table to div - javascript

I created a table using d3.js library,
but when I try to append the table to a div, it gives an error?
code:
<head>
<script src="../../d3.min.js"></script>
</head>
<body>
<div id="main">
Hi
</div>
<script>
const table = d3.create("table");
const tbody = table.append("tbody");
var i,j,row;
for(i=0;i<5;i++){
row =tbody.append("tr");
for(j=0;j<3;j++){
row.append("td").text(`${i},${j}`);
}
}
console.log(typeof(table));
console.log(table);
node =table.node();
console.log(typeof(node));
console.log(node);
d3.select("#main").append(node);
</script>
</body>
</html>
but I get an error:
although my code similar to what is in this tutorial
A tutorial on d3js

Observable tutorials are meant to create Observable notebooks. There are several small differences between Observable and a regular D3 running in a browser.
That being said, the only problem in your approach is that append requires either a string with the tag name or the element. If you have a string, just use it as append("foo"). However, if you have the element to be appended (in your case, table.node()), you have to return it from a function.
So, instead of:
d3.select("#main").append(node);
It has to be:
d3.select("#main").append(() => node);
Here is your code with that change only:
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div id="main">
Hi
</div>
<script>
const table = d3.create("table");
const tbody = table.append("tbody");
var i, j, row;
for (i = 0; i < 5; i++) {
row = tbody.append("tr");
for (j = 0; j < 3; j++) {
row.append("td").text(`${i},${j}`);
}
}
node = table.node();
d3.select("#main").append(() => node);
</script>
Finally, if you are writing regular scripts for a browser, just ditch this d3.create() followed by append(() => selection.node()). Use a simple tag string instead.

Related

removing tag name but keep tag

Hello I have a <strong></strong> Tag nested in a paragraph <p></p>, I'm trying to remove the <strong> tag but keep the text or the value. Something similar to unwrapping in jquery but in javascript.
I tried this code on a dummy HTML page and it works fine
<html>
<body>
<p>aaa <Strong>bbbbb</Strong></p>
<p>acccaa <Strong>ddddd</Strong></p>
<p>eeee <Strong>ffff</Strong></p>
<script>
var p = document.getElementsByTagName("p");
for(var i=0;i<p.length;i++){
var strongs = p[i].getElementsByTagName("strong");
for(var j=0;j<strongs.length;j++){
p[i].replaceChild(document.createTextNode(strongs[j].innerText),strongs[j]);
}
}
</script>
</body>
</html>
But as soon as I try the same code on a real page example: https://www.bustle.com/privacy
I get this error:
Failed to execute 'replaceChild' on 'Node': The node to be replaced is not a child of this node.
Any idea on how to get this to work on the example or any other example?
getElementsByTagName() returns a live NodeList. So when you replace a tag, the indexes of all the following elements shift down and the code fails when you have more than one <strong> tag in the same paragraph. As a result, it will skip some tags.
The solution is to convert the NodeList to an array so it doesn't change while you're looping.
Another problem in your real page that isn't in the snippet is that the <strong> tags can be nested deeply within the <p>. You should use strongs[j].parentElement to get its direct parent, rather than assuming that the p[i] is the parent.
var p = document.getElementsByTagName("p");
for (var i = 0; i < p.length; i++) {
var strongs = Array.from(p[i].getElementsByTagName("strong"));
for (var j = 0; j < strongs.length; j++) {
strongs[j].parentElement.replaceChild(document.createTextNode(strongs[j].innerText), strongs[j]);
}
}
<html>
<body>
<p>aaa
<Strong>bbbbb</Strong> - <strong>12345</strong></p>
<p>acccaa <span><Strong>ddddd</Strong> x</span></p>
<p>eeee
<Strong>ffff</Strong>
</p>
</body>
</html>
You can also avoid the nested loops by using a query selector.
var strongs = document.querySelectorAll("p strong");
strongs.forEach(strong => strong.parentElement.replaceChild(document.createTextNode(strong.innerText), strong));
<html>
<body>
<p>aaa
<Strong>bbbbb</Strong> - <strong>12345</strong></p>
<p>acccaa <span><Strong>ddddd</Strong> x</span></p>
<p>eeee
<Strong>ffff</Strong>
</p>
</body>
</html>
No need to loop through paragraphs to remove <strong>. Simply removing all 'strongs' in place works fine.
function removeStrongs() {
let strongs = document.querySelectorAll('strong');
strongs.forEach(strong => {
strong.insertAdjacentText('afterend', strong.innerText);
strong.remove();
});
}
<h4>This is a <strong>Title</strong></h4>
<p>
Now is the time for all <strong>good</strong> men to come to the <strong>aid</strong> of the party.
</p>
<p>A <strong>quick brown</strong> fox jumps over the lazy dog.</p>
<button onclick="removeStrongs();">Remove Strongs</button>

HTML Template displaying an extra template item when added via Javascript

I have an HTML template that I populate using Javascript when the on-load event is called and for some reason, the base template itself displays despite not being part of the array I loop through and I can't figure out why. From my understanding templates only show when appended to the document body, but the base template shows in addition to the appended templates (minus the last one since the base template hogs the first spot) and I can verify this by changing data in the base template.
I cannot use a jQuery solution as this is going to be read into an iOS web view through a function that only accepts pure Javascript.
My code is as follows, if anyone can explain why the initial template is showing itself I would greatly appreciate it. I've scoured for a solution and haven't found anything and think maybe I'm misunderstanding how the templating works.
<script>
var titles = ["Item1", "Item2", "Item3", "Item4", "Item5"];
function addGalleryItem() {
var template = document.querySelector("#galleryTemplate");
var label = template.content.querySelector(".caption");
var node;
for (var i = 0; i < titles.length; i++) {
node = document.importNode(template.content, true);
label.innerHTML = titles[i];
document.body.appendChild(node);
}
}
</script>
<html>
<body onload="addGalleryItem()">
<template id="galleryTemplate">
<div class="galleryItem">
<img class="galleryImage" src="img.png" alt="Unknown-1" width="275" height="183">
<div class="caption">
<label></label>
</div>
</div>
</template>
</body>
</html>
I think, the only thing wrong with your code is you are assigning innerhtml to the 'label' variable after 'node' variable, so the right code is
for (var i = 0; i < titles.length; i++) {
label.innerHTML = titles[i];
node = document.importNode(template.content, true);
document.body.appendChild(node);
}
template is not rendering, your loop is running 5 times as it has 5 items.
is it as simple as :
<!DOCTYPE html>
but I also think you are querying your label in the wrong place:
for (var i = 0; i < titles.length; i++) {
node = document.importNode(template.content, true);
label = node.querySelector(".caption"); // should query here ?
label.innerHTML = titles[i];
document.body.appendChild(node);
}

Text to Html list

I want to make a html which will auto get the information for a *.txt file
something1
something2
something3
and output into this html file
<!DOCTYPE html>
<html>
<head>
<title>List</title>
</head>
<body>
<ul>
#here to output#
</ul>
</body>
</html>
I prefer to use JavaScript. Some help is appreciated.
You have to request the file using AJAX call. Then you need to iterate through each line of response and generate DOM element (li in this case) and input line inside of it. After that insert each li element into your ul list.
You can achieve it using jQuery as you are probably new to JavaScript it's probably the easiest way.
What you need to do is request the file first:
$.ajax('url/to/your/file', {
success: fileRetrieved
});
Now after the file is retrieved jQuery will call fileRetrieved method, so we have to create it:
function fileRetrieved(contents) {
var lines = contents.split('\n');
for(var i = 0; i < lines.length; i += 1) {
createListElement(lines[i]);
}
}
Now for each line from the file function fileRetrieved will call createListElement function passing line of text to it. Now we just need to generate the list element and inject it into DOM.
function createListElement(text) {
var into = $('ul');
var el = $('<li></li>').html(text);
el.appendTo(into);
}
Of course you don't want to retrieve into element each time createListElement is called so just store it somewhere outside the function, it's your call, I'm just giving you the general idea.
Here is an example of the script (without AJAX call of course as we can't simulate it):
var into = $('#result');
function fileRetrieved(contents) {
var lines = contents.split('\n');
for (var i = 0; i < lines.length; i += 1) {
createListElement(lines[i]);
}
}
function createListElement(text) {
var el = $('<li></li>').html(text);
el.appendTo(into);
}
var text = $('#text').html();
fileRetrieved(text);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!-- This element simulates file contents-->
<pre id="text">
fdsafdsafdsa
fdsafd
safdsaf
dsafdsaf
dsafdsafds
afdsa
</pre>
<div id="result"></div>
Try this
<html>
<head>
<title>List</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
</head>
<body>
<ul id="renderTxt_list">
</ul>
<input type="button" id="lesen" value="Read Data" />
</body>
<script>
$(document).ready(function(){
$("#lesen").click(function() {
$.ajax({
url : "testTxt.txt",
dataType: "text",
success : function (data) {
$html = "";
var lines = data.split("\n");
for (var i = 0, len = lines.length; i < len; i++) {
$html += '<li>'+lines[i]+'</li>';
}
$("body ul").append($html);
}
});
});
});
</script>
</html>
You need to request the file first, and then append it to your chosen place in the document.
You can for example use jQuery's get (or any other function like the native fetch), and then inject it into the ul element:
$.get("*.txt").then(x => $("ul").html("<li>" + x.split('\n').join('</li><li>') + "</li>"))
Let's break this solution by steps:
First, we need to request the external file:
$.get("*.txt")
Read about jQuery's get here. Basicly it will request the file you asked for using network request, and return a promise.
In the Promise's then, we can do stuff with the request's result after it is resolved. In our case we want to first break it by lines:
x.split('\n')
split will return an array that will look like this: ["line 1, "line 2", "line 3"].
JS arrays have the join method, which concat them to string while putting the string you want between the items. So after we do this:
x.split('\n').join('</li><li>')
We only need to add the <li> element to the start and end of the string like this:
"<li>" + x.split('\n').join('</li><li>') + "</li>"
Finally we appent it to your chosen element using jQuery's html.

Get all html tags with Javascript

Does anybody knows how can I get all the HTML tags that exist in a page?
I need to get only the tags without their IDs or other attributes, and create a kind of tree-structure of them.
Prefer to do that with Javascript or JQuery.
For example, This HTML code:
<html>
<head>
<title>
Example Page
</title>
</head>
< body>
<h1 style="somestyle">
Blabla
</h1>
<div id="id">
<table id="formid">
<tr>
<td>
</td>
</tr>
</table>
</div>
</body>
</html>
should return return:
html
head
title
body
h1
div
table
tr
td
You can pass a * to getElementsByTagName() so that it will return all elements in a page:
var all = document.getElementsByTagName("*");
for (var i=0, max=all.length; i < max; i++) {
// Do something with the element here
}
Its a very simple piece of Javascript
document.querySelectorAll('*')
Try it out in the console log and it will show you all the tags in the document.
Another example is to getElementsByTagName
These do print out into an array, so you can then loop through the elements and doing different things on different elements.
Example:
var items = document.getElementsByTagName("*");
for (var i = 0; i < items.length; i++) {
//do stuff
}
I did it with getElementsByTagName and .tagName for every value in the return array.

How to do page numbering in header/footer htmls with wkhtmltopdf?

I'm developing an electronic invoicing system, and one of our features is generating PDFs of the invoices, and mailing them. We have multiple templates for invoices, and will create more later, so we decided to use HTML templates, generate HTML document, and then convert it to PDF. But we're facing a problem with wkhtmltopdf, that as far as I know (I've been Googleing for days to find the solution) we cannot simply both use HTML as header/footer, and show page numbers in them.
In a bug report (or such) ( http://code.google.com/p/wkhtmltopdf/issues/detail?id=140 ) I read that with JavaScript it is achievable this combo. But no other information on how to do it can be found on this page, or elsewhere.
It is, of course not so important to force using JavaScript, if with wkhtmltopdf some CSS magic could work, it would be just as awesome, as any other hackish solutions.
Thanks!
Actually it's much simpler than with the code snippet. You can add the following argument on the command line: --footer-center [page]/[topage].
Like richard mentioned, further variables are in the Footers and Headers section of the documentation.
Among a few other parameters, the page number and total page number are passed to the footer HTML as query params, as outlined in the official docs:
... the [page number] arguments are sent to the header/footer html documents in GET fashion.
Source: http://wkhtmltopdf.org/usage/wkhtmltopdf.txt
So the solution is to retrieve these parameters using a bit of JS and rendering them into the HTML template. Here is a complete working example of a footer HTML:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<script>
function substitutePdfVariables() {
function getParameterByName(name) {
var match = RegExp('[?&]' + name + '=([^&]*)').exec(window.location.search);
return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
}
function substitute(name) {
var value = getParameterByName(name);
var elements = document.getElementsByClassName(name);
for (var i = 0; elements && i < elements.length; i++) {
elements[i].textContent = value;
}
}
['frompage', 'topage', 'page', 'webpage', 'section', 'subsection', 'subsubsection']
.forEach(function(param) {
substitute(param);
});
}
</script>
</head>
<body onload="substitutePdfVariables()">
<p>Page <span class="page"></span> of <span class="topage"></span></p>
</body>
</html>
substitutePdfVariables() is called in body onload. We then get each supported variable from the query string and replace the content in all elements with a matching class name.
To show the page number and total pages you can use this javascript snippet in your footer or header code:
var pdfInfo = {};
var x = document.location.search.substring(1).split('&');
for (var i in x) { var z = x[i].split('=',2); pdfInfo[z[0]] = unescape(z[1]); }
function getPdfInfo() {
var page = pdfInfo.page || 1;
var pageCount = pdfInfo.topage || 1;
document.getElementById('pdfkit_page_current').textContent = page;
document.getElementById('pdfkit_page_count').textContent = pageCount;
}
And call getPdfInfo with page onload
Of course pdfkit_page_current and pdfkit_page_count will be the two elements that show the numbers.
Snippet taken from here
From the wkhtmltopdf documentation (http://madalgo.au.dk/~jakobt/wkhtmltoxdoc/wkhtmltopdf-0.9.9-doc.html) under the heading "Footers and Headers" there is a code snippet to achieve page numbering:
<html><head><script>
function subst() {
var vars={};
var x=document.location.search.substring(1).split('&');
for(var i in x) {var z=x[i].split('=',2);vars[z[0]] = unescape(z[1]);}
var x=['frompage','topage','page','webpage','section','subsection','subsubsection'];
for(var i in x) {
var y = document.getElementsByClassName(x[i]);
for(var j=0; j<y.length; ++j) y[j].textContent = vars[x[i]];
}
}
</script></head><body style="border:0; margin: 0;" onload="subst()">
<table style="border-bottom: 1px solid black; width: 100%">
<tr>
<td class="section"></td>
<td style="text-align:right">
Page <span class="page"></span> of <span class="topage"></span>
</td>
</tr>
</table>
</body></html>
There are also more available variables which can be substituted other than page numbers for use in Headers/Footers.
Safe approach, even if you are using XHTML (for example, with thymeleaf). The only difference with other's solution is the use of // tags.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<script>
/*<![CDATA[*/
function subst() {
var vars = {};
var query_strings_from_url = document.location.search.substring(1).split('&');
for (var query_string in query_strings_from_url) {
if (query_strings_from_url.hasOwnProperty(query_string)) {
var temp_var = query_strings_from_url[query_string].split('=', 2);
vars[temp_var[0]] = decodeURI(temp_var[1]);
}
}
var css_selector_classes = ['page', 'topage'];
for (var css_class in css_selector_classes) {
if (css_selector_classes.hasOwnProperty(css_class)) {
var element = document.getElementsByClassName(css_selector_classes[css_class]);
for (var j = 0; j < element.length; ++j) {
element[j].textContent = vars[css_selector_classes[css_class]];
}
}
}
}
/*]]>*/
</script>
</head>
<body onload="subst()">
<div class="page-counter">Page <span class="page"></span> of <span class="topage"></span></div>
</body>
Last note: if using thymeleaf, replace <script> with <script th:inline="javascript">.
My example shows how to hide some text on a particular page, for this case it shows the text from page 2 onwards
<span id='pageNumber'>{#pageNum}</span>
<span id='pageNumber2' style="float:right; font-size: 10pt; font-family: 'Myriad ProM', MyriadPro;"><strong>${siniestro.numeroReclamo}</strong></span>
<script>
var elem = document.getElementById('pageNumber');
document.getElementById("pageNumber").style.display = "none";
if (parseInt(elem.innerHTML) <= 1) {
elem.style.display = 'none';
document.getElementById("pageNumber2").style.display = "none";
}
</script>
Right From the wkhtmltopdf Docs
Updated for 0.12.6.
Footers And Headers:
Headers and footers can be added to the
document by the --header-* and --footer* arguments respectively. In
header and footer text string supplied to e.g. --header-left, the
following variables will be substituted.
[page] Replaced by the number of the pages currently being printed
[frompage] Replaced by the number of the first page to be printed
[topage] Replaced by the number of the last page to be printed
[webpage] Replaced by the URL of the page being printed
[section] Replaced by the name of the current section
[subsection] Replaced by the name of the current subsection
[date] Replaced by the current date in system local format
[isodate] Replaced by the current date in ISO 8601 extended format
[time] Replaced by the current time in system local format
[title] Replaced by the title of the of the current page object
[doctitle] Replaced by the title of the output document
[sitepage] Replaced by the number of the page in the current site being converted
[sitepages] Replaced by the number of pages in the current site being converted
As an example specifying --header-right "Page [page] of [topage]", will result in the text "Page x of y" where x is the
number of the current page and y is the number of the last page, to
appear in the upper left corner in the document.
Headers and footers can also be supplied with HTML documents. As an
example one could specify --header-html header.html, and use the
following content in header.html:
<!DOCTYPE html>
<html>
<head><script>
function subst() {
var vars = {};
var query_strings_from_url = document.location.search.substring(1).split('&');
for (var query_string in query_strings_from_url) {
if (query_strings_from_url.hasOwnProperty(query_string)) {
var temp_var = query_strings_from_url[query_string].split('=', 2);
vars[temp_var[0]] = decodeURI(temp_var[1]);
}
}
var css_selector_classes = ['page', 'frompage', 'topage', 'webpage', 'section', 'subsection', 'date', 'isodate', 'time', 'title', 'doctitle', 'sitepage', 'sitepages'];
for (var css_class in css_selector_classes) {
if (css_selector_classes.hasOwnProperty(css_class)) {
var element = document.getElementsByClassName(css_selector_classes[css_class]);
for (var j = 0; j < element.length; ++j) {
element[j].textContent = vars[css_selector_classes[css_class]];
}
}
}
}
</script></head>
<body style="border:0; margin: 0;" onload="subst()">
<table style="border-bottom: 1px solid black; width: 100%">
<tr>
<td class="section"></td>
<td style="text-align:right">
Page <span class="page"></span> of <span class="topage"></span>
</td>
</tr>
</table>
</body>
</html>
ProTip
If you are not using certain information like the webpage, section, subsection, subsubsection, then you should remove them. We are generating fairly large PDFs and were running into a segmentation fault at ~1,000 pages.
After a thorough investigation, it came down to removing those unused variables. No we can generate 7,000+ page PDFs without seeing the Segmentation Fault.
I have not understood the command line en finally I find the solution to put this information directly in the controller without any JS en command line.
In my controller when I call the format.pdf I just put the line footer:
format.pdf do
render :pdf => "show",
page_size: 'A4',
layouts: "pdf.html",
encoding: "UTF-8",
footer: {
right: "[page]/[topage]",
center: "Qmaker",
},
margin: { top:15,
bottom: 15,
left: 10,
right: 10}
end
The way it SHOULD be done (that is, if wkhtmltopdf supported it) would be using proper CSS Paged Media: http://www.w3.org/TR/css3-gcpm/
I'm looking into what it will take now.

Categories

Resources