Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl-Sensitive Sunglasses

Identifying CSS widows and orphans

by wfsp (Abbot)
on Sep 06, 2004 at 16:11 UTC ( #388829=perlquestion: print w/replies, xml ) Need Help??
wfsp has asked for the wisdom of the Perl Monks concerning the following question:

Finding a style name that is in the style sheet but not in any html file (widow) or a style used in html files that is not in the style sheet (orphan).

Parsing a local copy of a web site on winXP with Activestate.

Initialise hash with style names from the style sheet,
Extract styles used by html and add file names to each style key and
Output report.

Orphans can be identified while parsing the html file (the class key won't exist) and widows during output (usage count will be zero). It is often useful to identify rarely used styles, say less than 10 occurances. The output loop can trap these if required. As is, it generates 18k records which is a tad excessive so I either add more filters or use other scripts to filter areas of concern.

The style sheet only uses simple styles in the form of:
.redlink:hover etc.
(i.e. anything between a dot and an optional hyphon)

Web site overview:
approx 2k html pages (30MB). More details on my scratchpad.

This is part of an attempt to satisfy CSS2. I'm just hoping I can get there before CSS3 comes out! I don't have access to peer review so would appreciate any comments. I realise that it will be difficult to test without a handy web site on your hard disk but any general observations will be welcome.


#!/bin/perl5 use strict; use warnings; use HTML::TokeParser::Simple; use File::Find; use CSS::Tiny; # use diagnostics; # use Data::Dumper; my $dir = q|c:/web/root/|; # initialise hash with class names from the style sheet my $css = CSS::Tiny->new(); $css = CSS::Tiny->read( "${dir}cwi.css" ) or die "can't open style sheet $!"; my %class_hash; for my $style ( keys %{$css} ){ # a class name lies beween a dot and an optional hyphen $class_hash{$_}{'zz total'} = 0 if ($_) = $style =~ /\.([^:]*)/ ; } find(\&action, $dir); # generate report open my $o, '>', 'classes.txt' or die "can't open file for output $!"; for my $class ( sort keys %class_hash ){ my $total = $class_hash{$class}{'zz total'}; # next if $total > 10; my $widow = $total ? '' : 'widow'; print $o $class, "\t", $widow, "\n"; for my $path ( sort keys %{$class_hash{$class}} ){ print $o "\t$path\t$class_hash{$class}{$path}\n"; } } close $o; print "done\n"; sub action { # extract class names from each html file return if -d and ( /^_/ or /^pics/ ) and do { $File::Find::prune = 1 }; return unless -f and /\.html$/; my $tp = HTML::TokeParser::Simple->new( $_ ) or die "error opening html file $!"; while ( my $t = $tp->get_token ) { my $classes = $t->return_attr( 'class' ); if ( $classes ){ (my $path = $File::Find::name) =~ s/$dir//; # a class attribute may contain more than one class my @class_array = split ' ', $classes; for my $class (@class_array){ unless ( exists $class_hash{$class} ){ $class_hash{$class}{'zz orphan'}++; } $class_hash{$class}{$path}++; $class_hash{$class}{'zz total'}++; } } } }
Example output:
(indent2 is a widow and indent3 is an orphan)
indent2 widow contents.html 15 publications/tt/contents.html 15 zz total 30 indent3 contents.html 35 zz orphan 35 zz total 35 intro dutch/home.html 1 eng/2003/02/24history.html 2 french/home.html 1 german/home.html 1 home.html 1 italian/home.html 1 portugese/home.html 1 redirect.html 1 spanish/home.html 1 swe/home.html 1 turkish/home.html 1 zz total 12 langpage dutch/2003/1410.html 1 dutch/2003/20030330resist.html 1 dutch/2003/20030424.html 1 dutch/2003/20030730.html 1 dutch/2003/20030911.html 1 dutch/2003/20031014.html 1 dutch/2003/template.html 1

This all assumes, of course, that all styles are in one external sheet (which they are in this case).

Update 2:
Added clarification.

Replies are listed 'Best First'.
Re: Identifying CSS widows and orphans
by Your Mother (Chancellor) on Sep 06, 2004 at 20:24 UTC

    This seems like a decent approach. Your vocabulary is difficult to follow. Widows and orphans are typographical terms (and CSS is largely concerned with typographical info) and you seem to be using them as tree terms (what's there, what's not, when it's used). Real widows and orphans are still nigh impossible to deal with in HTML b/c browsers, even with specific layouts, aren't pixel perfect and certainly don't implement drawing in predictable ways from one to another; and pagination isn't really an issue unless the page is going to the printer.

    One perl note (other monks are better qualified to find these if there are more):

    $style =~ /\.([^:]*)/ ;
    Should probably be something more like:
    $style =~ /\.((?:[^:]|[-\w])+)/;
    Otherwise "." or ".#!$@" are style names that could be picked up. And even the way I've got it is too simplistic, it could pick up invalid names. The inplace modification of the $_ is probably more confusing than it needs to be.

    Good luck. If you get a final version you like, you might post it to the code catacombs.

      Your vocabulary is difficult to follow.

      Yes. As a printer, I'm familiar with the terms which is probably why they came to mind. I should have used something else. Too close to it for too long. I knew what I meant!

      I've added an explantion (should have done that the first time). Thanks for the comments.


Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://388829]
Approved by Old_Gray_Bear
[Lady_Aleena]: shmem, I feel like an idiot for forgetting something so basic.
shmem puts a big cauldron of "silly con charme" on the table in the refectorium
[reisinge]: 'push @foo, @bar' is the same as '@foo = (@foo ,@bar)' ?
[shmem]: yes, at least for the result in @foo
[shmem]: but the results of both operations are different.

How do I use this? | Other CB clients
Other Users?
Others examining the Monastery: (9)
As of 2017-04-27 11:35 GMT
Find Nodes?
    Voting Booth?
    I'm a fool:

    Results (503 votes). Check out past polls.