Counting DOM elements with CSS - not working consistently - css

As part of some performance testing, I want to see how many DOM elements there are within a page at any given time.
Initially I used the console and manually typed: document.getElementsByTagName('*').length
But being that elements are dynamically added and removed, I wanted a way to show this automatically without having to do this manually.
Using CSS counters seemed to be the easiest way for me to do this:
(Here's a demo: see the yellow box in the top right)
html {
counter-reset: elems;
counter-increment: elems;
}
html * {
counter-increment: elems;
}
body:after {
content: counter(elems) ' elements';
position: absolute;
top:0;
right: 0;
padding: 0.5rem;
background-color: yellow;
z-index: 1000000000; /* make sure display appears on top */
}
html + head + body = 3... +
A section with 4 divs = 5..... 3 + 5 = 8
<section>
<div>div1</div>
<div>div2
<div>div3</div>
</div>
<div>div4</div>
<section>
The problem is that this technique is not working for some more complex websites, and i'm guessing that it's because it's somehow naive.
Take for instance wikipedia's main page - I used the browser inspector to add the above css-counter css and that produced "888 elements"
However when I used the console document.getElementsByTagName('*').length - it returned 975 elements.
So my question is:
What is causing this huge discrepancy in the number of DOM elements, is there something I haven't taken into account?
Can this be done reliably with CSS?

The reason you get different counts is because your counter-increment: elems; rule is lower priority than some existing counter-increment rules in Wikipedia's stylesheet, so there are elements which increment other counters (one example is named "listitem") but not yours.
You'd have to make your selector more specific and override Wikipedia's, or add the website's own counter (or write some JavaScript to add your own counter in the existing counter-increment rules, to avoid breaking the existing style).

What is causing this huge discrepancy in the number of DOM elements,
is there something I haven't taken into account?
Yes, elements which have display:none applied are ignored by css counters.
From the spec:
An element that does not generate a box (for example, an element with
display set to none, or a pseudo-element with content set to none)
cannot set, reset, or increment a counter. The counter properties are
still valid on such an element, but they must have no effect.
Demo:
html {
counter-reset: elems;
counter-increment: elems; /* Include the html element as well */
}
html * {
counter-increment: elems;
}
body:after {
content: counter(elems) ' elements';
position: absolute;
top:0;
right: 0;
padding: 0.3rem;
background-color: lightyellow;
z-index: 1000000000; /* make sure display appears on top */
}
.hidden {
display: none;
}
html + head + body = 3... +
A section with 4 divs = 5..... 3 + 5 = 8
<section>
<div>div1</div>
<div>div2
<div>div3</div>
</div>
<div>div4</div>
<div class="hidden"></div>
<div class="hidden"></div>
<div class="hidden"></div>
<div class="hidden"></div>
<div class="hidden"></div>
<div class="hidden"></div>
<section>
On the other hand document.getElementsByTagName('*').length WILL count elements with display:none
This may only be one of the differences which I hadn't taken into account, but I'd say it's probably the main culprit as to the discrepancy.

Related

Is it possible to negate an element from receiving display:none using the :not pseudo-class in CSS?

<html>
<body>
<nav>....</nav>
<article>more things...</article>
<div id="myModal" class="modal">contents</div>
<body>
</html>
In linked CSS file (using Chrome):
#media print{
body:not(#myModal){
display:none;
visibility:hidden;
}
#myModal{ /*shouldn't have to but doesn't work anyway */
display:block;
visibility:visible;
}
}
This does not work. I am trying to get rid of everything behind the modal for printing without scripting. Apparently that is not possible. Can a display:none :not not negate elements contained within the container?
Edit: I have looked here, but cannot find the answer. https://www.w3.org/TR/css3-selectors/#negation
Edit: I want to hide everything except the modal. But display:none keeps that from happening. It does it for the whole body element, regardless of my negation.
Edit: Whatever it is, it does not work in the media call, so my current idea is to move the div. I thought there might be a better way. Edit: I also need display:none because print will still print the blank pages where the elements are hidden. display will remove those elements and allow me to print my modal without a bunch of blank pages of hidden elements.
display: none doesn't load the element or it's children. To Chrome, Firefox, etc., #myModal doesn't exist. Consequently, you can't use display: none as the way to did.
There are other alternatives though:
Option 1
#media print {
* {
visibility: hidden;
height: 0 !important; /* added !important with info from update */
}
#myModal {
visibility: visible;
height: auto !important;
}
}
<body>
<nav>....</nav>
<article>more things...</article>
<div id="myModal" class="modal">contents</div>
<button onclick="window.print();">Print</button>
<body>
Option 2 This probably won't work with your new update.
#media print {
body > *:not(#myModal) {
display: none;
}
}
<body>
<nav>....</nav>
<article>more things...</article>
<div id="myModal" class="modal">contents</div>
<button onclick="window.print();">Print</button>
<body>

How to break up column layouts in Firefox without column-span?

I have just discovered that Firefox doesn't support the CSS property 'column-span' yet. Without 'column-span', breaking up column layouts seems unworkable. Is there a workaround to achieve the same result?
column-span is not supported on Firefox yet. However, you might be able to workaround this with your HTML structure.
Let's say you need a column-span:2 headline and the paragraphs should have 2 columns instead.
Like:
<article>
<p>Introduction - this should span everything too</p>
<h2>This should span everything</h2>
<p>this text should be multicolumn</p>
<h2>This should span everything again</h2>
<p>this text should be multicolumn again</p>
</article>
The logic approach is to do something like:
article {
column-count: 2;
}
article > p:nth-child(1) {
column-span: all;
}
article > h2 {
column-span: all;
}
But as mentioned, this breaks horribly in Firefox. It also causes some issues with text sometimes being cut off on Safari. As the date of writing, only Chrome shows a satisfying result.
In this case you can avoid column-span completely:
article {
// no need
}
article > p:nth-child(1) {
// no need
}
article > p:nth-child(n+2) {
column-count: 2;
}
article > h2 {
// no need
}
You can apply the style directly on the paragraphs.
The result looks fine on Firefox, Safari and Chrome. And it's less code. So maybe you can adjust your HTML a little so you simply don't rely on column-span at all.
Here's what worked for me:
#supports not (column-span: all) {
.some-css-class {
position: absolute;
left: 0;
top: -10;
width: 100%;
}
}
I based my solution off the code presented here: https://css-tricks.com/forums/topic/any-ideas-for-firefox-column-span-solution/. You can try using top: 0 but I made the value negative because top: 0 caused the element to appear at the very top of the page instead of below the two columns as I intended (which any negative value seemed to fix).

Why does IE freeze on Layout when rendering an empty SVG element?

I am currently hitting an issue in IE 10 and 11 where the browser tab is hanging every now and then on Layout in the UI Responsiveness tool. I am part of a team writing a fairly large knockout.js web app, so nailing down the exact condition that is creating this issue has been extremely difficult. From what I can tell, the browser tab hangs when Layout is performed when the removal of loading indicator HTML is removed from the page and some divs plus an empty SVG tag is appended to the DOM in its place.
I have been able to nail down that the empty SVG tag is the culprit, but I do not know why and I cannot remove that tag from the page is it is an important element to a D# data visualization that I am trying to create.
Here is the US Responsiveness report that IE 11 has provided me. I have zoomed in on the problematic area, and as you can see in the picture, the Layout thread spikes the CPU to 100%.
Before I get into the code samples my question is:
Why would the browser tab intermittently freeze/hang from adding an empty SVG element to the page?
The HTML gets appended to the DOM via javascript in as minimal of a way as possible from my research on reducing reflow in the browser:
var contentHTML = "";
contentHTML += '<div class="axis-title y-axis-title">' + renderString(bindingData.yAxis.title) + "</div>";
contentHTML += '<div class="' + CANVAS_CLASS + '"></div>';
contentHTML += '<svg class="x-axis"></svg>'; // The problematic element
element.innerHTML = contentHTML;
This results in the following HTML (note: all of the data-bind stuff is for knockout.js binding handlers, which triggers the JS above):
<div class="chart" data-bind="
barChart: {
data: rowData,
categoryTextKey: 'label',
valueKey: 'keyOnObject',
xAxis: {
title: 'xAxisTitle',
domain: [-1, 1]
},
yAxis: {
title: 'yAxisTitle'
},
onClick: onLabelClick,
formatValueText: formatPercentage
}
"></div>
<div class="axis-title y-axis-title">Y Title</div>
<div class="chart-canvas"></div>
<svg xmlns="http://www.w3.org/2000/svg" class="x-axis" />
<div class="axis-title x-axis-title">X Title</div>
</div>
Lastly, I also am using flexbox CSS rules to lay out my HTML. I am not sure if that is affecting this issue, but here is the CSS in case it helps:
.chart {
.flexbox();
.flex-direction(column);
height: 100%;
width: 100%;
.chart-label-click {
cursor: pointer;
}
.chart-header,
.axis-title,
.x-axis {
.flex-grow(0);
.flex-shrink(0);
}
.chart-canvas {
.flex-grow(1);
.flex-shrink(1);
overflow-x: hidden;
overflow-y: auto;
width: 100%;
}
.chart-canvas svg {
display: block;
width: 100%;
}
.axis-title {
font-weight: bold;
}
.x-axis {
.flexbox();
.flex-grow(0);
.flex-basis(20px);
margin-bottom: 5px;
overflow: visible;
width: 100%;
}
.x-axis line,
.x-axis path {
fill: none;
stroke: #d1d1d1;
stroke-width: 1px;
shape-rendering: crispEdges;
}
}
Thank you for any help you may have. I am not sure how to nail this down is it is intermittent in one section of our app and our codebase is pretty big to figure out the exact combination of code in other files that may also be contributing to this issue.
The described issue seems to be this bug:
https://connect.microsoft.com/IE/feedback/details/796745/mouse-events-are-not-delivered-at-all-anymore-when-inside-an-svg-a-use-is-removed-from-the-dom
In the comments is a workaround described which at least worked for us:
You have to set style="pointer-events: none;" on the use elements.
Or simply add this to your css:
svg use { pointer-events:none; }
But be aware that this also disables any mouse events triggered on the use element.
The way I ultimately fixed this issue was to remove the use of display:flex on the .chart element. In its place, I used a fixed height and display:block. It looks like this is ultimately a bug w/ IE when mixing SVG and flexbox together.
Make sure your code isn't setting a value in JavaScript (or other language) without even the quotes such as the following...
var a = ;//[var][space][a][space][=][space][;]
That will freeze up IE11 (not sure about 10 offhand).
After many days of searching I decided to solve the problem in addressing this:
svg use { pointer-events:none; }

Browser Support for CSS Page Numbers

So I am aware of this option: Page numbers with CSS/HTML.
It seems by far to be the best way to add page numbers to a print version of a page, but I can't get any variation of this to work anywhere. I have tried on my Windows 7 machine in Chrome, Firefox, and IE9. Based on some of the links it looks like this may be supported in more proprietary software like Prince XML. Is this supported by web browsers for print versions?
I have tried making just a blank html file and in the head adding this between two style tags:
#page {
#bottom-right {
content: counter(page) " of " counter(pages);
}
}
I have also simplified it even to just use content: "TEXT"; to see if I can get something to show up. Is this supported anywhere? By 'this' I'm specifically meaning the #page and #bottom-right tags, since I have gotten content to work many times.
I've been trying to implement paged media as well and have found, according to this Wikipedia page, that there's no browser support for margin boxes as yet. No wonder it wouldn't work!
http://en.wikipedia.org/wiki/Comparison_of_layout_engines_(Cascading_Style_Sheets)
See the table, Grammar and Rules, margin boxes section. Margin boxes are what's needed for page numbering as well as running headers and footers. Getting this implemented would save me the overhead of having to convert the printed media to PDF.
Not using #page, but I have gotten pure CSS page numbers to work in Firefox 20:
http://jsfiddle.net/okohll/QnFKZ/
To print, right click in the results frame (bottom right) and select
This Frame -> Print Frame...
The CSS is
#content {
display: table;
}
#pageFooter {
display: table-footer-group;
}
#pageFooter:after {
counter-increment: page;
content: counter(page);
}
and the HTML is
<div id="content">
<div id="pageFooter">Page </div>
multi-page content here...
</div>
This does not seem to work anymore. Appears it only worked for a short time and browser support was removed!
Counters have to be reset before they can be used, according to https://developer.mozilla.org/en-US/docs/CSS/Counters.
You can set your starting number to whatever, the default is 0.
Example:
#page {
counter-increment: page;
counter-reset: page 1;
#top-right {
content: "Page " counter(page) " of " counter(pages);
}
}
... in theory. In real world only PrinceXML supports this.
Via Mozilla, (Printing a document)
This puts a header and footer on each printed page. This works well in Mozilla, but not quite so well in IE and Chrome.
<!DOCTYPE html>
<html>
<head>
<title>Print sample</title>
<link rel="stylesheet" href="style4.css">
</head>
<body>
<h1>Section A</h1>
<p>This is the first section...</p>
<h1>Section B</h1>
<p>This is the second section...</p>
<div id="print-head">
Heading for paged media
</div>
<div id="print-foot">
Page:
</div>
</body>
</html>
The CSS:
/*** Print sample ***/
/* defaults for screen */
#print-head,
#print-foot {
display: none;
}
/* print only */
#media print {
h1 {
page-break-before: always;
padding-top: 2em;
}
h1:first-child {
page-break-before: avoid;
counter-reset: page;
}
#print-head {
display: block;
position: fixed;
top: 0pt;
left:0pt;
right: 0pt;
font-size: 200%;
text-align: center;
}
#print-foot {
display: block;
position: fixed;
bottom: 0pt;
right: 0pt;
font-size: 200%;
}
#print-foot:after {
content: counter(page);
counter-increment: page;
}
If you are looking to add page numbers when printing under Chrome/Chromium, one easy solution is to use Paged.js.
This JS library takes your HTML/CSS and cuts it into pages, ready to print as a book, that you will preview in your browser. It makes the #page and most the CSS3 specifications work for Chrome.
Solution 1 (easy) if you are OK with cutting your view into pages, ready to print
Just add their CDN in the head tag of your page :
<link href="path/to/file/interface.css" rel="stylesheet" type="text/css">
You can then add page numbers by using the automated counter page. Example :
HTML to put anywhere you want to display the current page number:
<div class="page-number"></div>
CSS to make the number appear in the div :
.page-number{
content: counter(page)
}
The library also allows to easily manage page margins, footers, headers, etc.
Solution 2 (trickier) if you want to show numbers (and page breaks) only when printing
In this case, you need to apply the Paged.js CDN only when printing the document.
One way I can think of would be to add a print me button that fires Javascript to :
add the CDN to the page
and then execute window.print(); to launch the printing prompt of the navigator

How to only show certain parts with CSS for Print?

I have a page with lots of data, tables and content.
I want to make a print version that will only display very few selected things.
Instead of writing another page just for printing, I was reading about CSS's feature for "#media print".
First, what browsers support it? Since this is an internal feature, it's OK if only the latest browsers support it.
I was thinking of tagging a few DOM elements with a "printable" class, and basically apply "display:none" to everything except those elements with the "printable" class.
Is that doable?
How do I achieve this?
EDIT:
This is what I have so far:
<style type="text/css">
#media print {
* {display:none;}
.printable, .printable > * {display:block;}
}
</style>
But it hides everything. How do I make those "printable" elements visible?
EDIT:
Trying now the negative approach
<style type="text/css">
#media print {
body *:not(.printable *) {display:none;}
}
</style>
This looks good in theory, however it doesn't work. Maybe "not" doesn't support advanced css ...
Start here. But basically what you are thinking is the correct approach.
Thanks, Now my question is actually
becoming: How do I apply CSS to a
class AND ALL OF ITS DESCENDANT
ELEMENTS? So that I can apply
"display:block" to whatever is in the
"printable" zones.
If an element is set to display:none; all its children will be hidden as well. But in any case. If you want a style to apply to all children of something else, you do the following:
.printable * {
display: block;
}
That would apply the style to all children of the "printable" zone.
If you want to display some links etc. when in the browser, that you don't want to be printed. Furthermore you have some logos and letterhead info that only should go on the printed page.
This seems to work fine:
Example:
CSS:
#media print {
.noPrint {
display:none;
}
}
#media screen {
.onlyPrint {
display: none;
}
}
HTML:
<div class="noPrint" id="this_is_not_printed" >
<a href=links.html>
</div>
<div class="onlyPrint" id="this_is_only_seen_on_printer" >
<img scr=logo.png >
<img scr=letterhead.png >
</div>
A simple way:
<style>
.print-only{
display: none;
}
#media print {
.no-print {
display: none;
}
.print-only{
display: block;
}
}
</style>
I got here because I was curious about printing a chart generated by chart.js. I wanted to just print the chart directly from the page (with a button that does a 'window.print') without all of the other content of the page.
So, I got closer by using the technique from the answer here: Why can't I override display property applied via an asterisk? .
You have to apply the 'asterisk' to the 'body' element, not just by itself. So, using the example CSS that the OP (Nathan) added to the question, I changed it to this:
<style type="text/css">
#media print {
body * {display:none;}
.printable, .printable > * {
display: block !important;
}
}
</style>
Then adding that 'printable' class to the chart itself, as in
<canvas id="myChart" class="printable" width="400" height="400"></canvas>
Which removed all page elements on the printed output except the chart when the 'print' button is clicked (via this):
<script>
myChart.render();
document.getElementById("printChart").addEventListener("click",function(){
window.print();
});
</script>
So, perhaps this will help anyone that gets to this question via the googles.
Came across the same question recently and for me, this solution works just perfect:
#media print {
* {
visibility: hidden;
}
.printable {
visibility: visible;
position: absolute;
top: 0;
left: 0;
padding: 10mm;
}
.printable * {
visibility: visible;
}
}
Since visibility: hidden doesn't remove elements, as display: none does, it is possible to change it for desired elements separately.
Nearly all browsers support it. It might be advantageous to use the media attribute on the link tag.
Using display: none; in some of your rules would be an appropriate way to handle your situation.
I suggest to hide the element that you won't print:
HTML
<h1 class="no-print" >Welcome Just Screen</h1>
<div> I want print this section :)</div>
<div class="no-print">It's display only on screen</div>
CSS
#media print {
.no-print {
display: none;
}
}

Resources