Hello and welcome to our community! Is this your first visit?
Register
Enjoy an ad free experience by logging in. Not a member yet? Register.
Results 1 to 5 of 5
  1. #1
    New to the CF scene
    Join Date
    Jul 2018
    Posts
    1
    Thanks
    0
    Thanked 0 Times in 0 Posts

    Unable to display full stylesheet in HTML window

    Hi, I just started learning JavaScript (via the ACM), but in a lesson about adding/removing CSS styles from a stylesheet, I came across a problem that i can't find a solution for. I copied out the code that was in the video, and have been over it multiple times, but inevitably end up with an error that the tutor didn't have.
    Essentially, in the video, the entire CSS stylesheet (attached in an external document) is displayed upon running this function:

    Code:
    function getSS() {
    		
    			var cssTemp = document.styleSheets[0].rules[0].cssText;
    			var cssTemp1 = "<b>Index #0</b><br>";
    			for (i = 0; i <= cssTemp.length; i++) {
    				char = cssTemp.substring(i, i+1);
    				if(char == "}") {
    					indexVal++;
    					cssTemp1 += char + "<br><b>Index #" + indexVal + "</b><br>";
    
    				} else if (char == "{" || char == ";") {
    					cssTemp1 += char = "<br>";
    				} else {
    					cssTemp1 += char;
    				}
    			}
    			document.getElementById("output").innerHTML = cssTemp1;
    			indexVal = 0;
    			
    		}
    However, when I run the function, I only ever get the first element displayed, rather than the whole page.

    Code:
    Index #0
    * 
    font-family: Segoe, "Segoe UI", "DejaVu Sans", "Trebuchet MS", Verdana, sans-serif
    font-size: 12px
    }
    Index #1


    I've already fixed one error, by adding in the .rules[0] before .cssText, so that the code would actually run in Chrome. I'm thinking it's either the fact that I'm including the index 0 there, or something in the for loop that ends the process earlier than I want, but I'm unable to parse it (and not sure why the same error didn't occur in the lesson).

    (It's also removing the "{" after * for some reason)

    I was hoping someone could give me some pointers on what might be happening (and apologies if the answer is obvious, I've not been at this long and this code is somewhat dense to me, plus I wasn't able to find any other lessons on displaying CSS stylesheets in the browser window).
    Last edited by vinyl-junkie; Jul 10th, 2018 at 03:38 PM. Reason: added code tags

  2. #2
    Master Coder Dormilich's Avatar
    Join Date
    Jan 2010
    Location
    Behind the Wall
    Posts
    5,776
    Thanks
    26
    Thanked 598 Times in 591 Posts
    According to the specification, there is no rules property on the CSSStyleSheet object ...

    cf. https://developer.mozilla.org/en-US/...API/StyleSheet & https://developer.mozilla.org/en-US/.../CSSStyleSheet
    The computer is always right. The computer is always right. The computer is always right. Take it from someone who has programmed for over ten years: not once has the computational mechanism of the machine malfunctioned.
    André Behrens, NY Times Software Developer

  3. #3
    Senior Coder deathshadow's Avatar
    Join Date
    Feb 2016
    Location
    Keene, NH
    Posts
    3,222
    Thanks
    4
    Thanked 470 Times in 458 Posts
    IT's "cssRules", not "Rules"

    Though the logic flow of that is totally banjaxed, and the methods of acting upon the stylesheets could be improved as well.

    First I'd probably have a loop to go through ALL the stylesheets, not just the first one.

    Code:
    for (var i = 0, sheet; sheet = document.styleSheets[i]; i++) {
    This is a construct of "for" you may not have seen before, but the way it works is that the middle "comparison" in a for loop can be ANY boolean compare, not just greather than or less than. That includes the 'loose' boolean result of an assignment (single =). As document.styleSheets is filled with objects and is a flattened iterable, if we try to access past the end of the "array-like" styleSheets it will return null/undefined, a loose false. Returning an object is loose true.

    Doing this gives us the handy "sheet" variable pointing into the index, and actually runs faster to boot!

    The 'rules' can be iterated in much the same manner.

    Code:
    for (var j = 0, rule; rule = sheet.cssRules[j]; j++) {
    When it comes to iterating through those rule strings to add your formatting there's a ... problem with that implementation. It doesn't compensate for UTF-8 encoding which COULD exist. This is why if you're going to go end-to-end through a string I suggest using the String.charAt property instead of indexing by array or substr, as it's UTF-8 and UTF-16 safe. It also returns a null/zero on access past the end of the string so we don't actually have to extract the length.

    Code:
    for (var k = 0, ch; ch = rule.cssText.charAt(k); k++) {
    Again iterating on the result of an assignment. Inside that loop, CH is the currently processed character.

    Another problem I see is the use of String addition to build up to an innerHTML. This has SO many issues that are hard to explain to beginners, but the bullet points are:

    1) That string increases the memory footprint unnecessarily.

    2) The longer a JavaScript string gets, the slower it gets.

    3) writing to innerHTML triggers the browser's parsing engine, which can introduce overhead, slowdowns, and unwanted side-effects such as re-triggering already handled events. If we go straight to the DOM with appendChild, createElement, and createTextNode we can avoid this.

    4) Writing strings to innerHMTL could result in writing out markup. If that CSS were to have code injected into it and you innerHTML it, you just made that code RUN. That's a massive security hole and another reason innerHTML should be avoided. If we were to createTextNode instead any code or content is automatically escaped to behave as text so this cannot happen.

    To that end I'd probably create a buffer string to build JUST the text between <br> tags into, a writeTag function to output things like headings and content-bearing tags, and a writeLine function to send the buffer as a textNode, flush the buffer, and create a BR element.

    Done properly we could even add utf-8 non-breaking spaces to it for indenting before and after {}.

    Code:
    	var
    		output = d.getElementById("output"),
    		buffer = '',
    		indent = 0;
    	
    	function writeTag(tagName, content) {
    		var e = d.createElement(tagName);
    		e.appendChild(d.createTextNode(content));
    		output.appendChild(e);
    	}
    	
    	function writeLine() {
    		output.appendChild(d.createTextNode(
    			"\u00a0".repeat(indent * 2) + buffer
    		));
    		output.appendChild(d.createElement('br'));
    		buffer = '';
    	}
    The markup you are outputting also needs some 'help', since "index #0" is NOT a legal entity name or other semantic reason for it to be 'bold'. It's clearly a heading describing the content that follows, so use a numbered heading tag of the appropriate depth!

    So... something like this:

    Code:
    	if (document.styleSheets.length) {
    		for (var i = 0, sheet; sheet = document.styleSheets[i]; i++) {
    			writeTag('h2', 'Stylesheet #' + i);
    			for (var j = 0, rule; rule = sheet.cssRules[j]; j++) {
    				writeTag('h3', 'Index #' + j);
    				indent = 0;
    				for (var k = 0, ch; ch = rule.cssText.charAt(k); k++) {
    					if (ch == '}') {
    						if (buffer = buffer.trimEnd()) writeLine();
    						indent--;
    						buffer = ch;
    						writeLine();
    					} else {
    						buffer += ch;
    						switch (ch) {
    							case '{':
    								writeLine();
    								indent++;
    								break;
    							case ';':
    								writeLine();
    						}
    					}
    				}
    				if (buffer) writeLine();
    			}
    		}
    	} else writeTag('p', 'No Stylesheets are present for this document');
    Assuming THE H1 is THE site title and this is its own set of subsections. If it's part of another section increase the heading depths as appropriate. Remember -- unless you are using HTML 5's idiotic pointless <section> tag -- THE H1 is THE heading under which everything on a SITE is the content of, an H2 indicates the start of a major subsection of the site, an H3 indicates the start of a subsection of the H2 before it, H4 is the start of subsection of the H3 preceding it, and so forth down the line to H6. HR even means "a change in topic or section where heading text is unwanted or unwarranted". They do NOT mean "fonts in different weights and sizes" or "draw a line across the page"!

    Finally I'd have all that wrapped in a SIF (Self Invoking Function, also known as a IIFE, Instantly Invoking Function Expression) so that scope is isolated hiding this from other scripts, and to pass "document" as "d" which actually speeds up execution (locals are looked up for before globals in the interpreter) and we're not typing "document" all over the damned place.

    So the final code would end up like this:

    http://www.cutcodedown.com/for_other...tyleDisplay.js


    Live demo here with both an external and internal stylesheet tested:

    Index of /for_others/LewisT/ - CutCodeDown

    Directory unlocked for easy access to the gooey bits and pieces. Feel free to nose around at other demos, rewrites, and samples I've written for other people over the past decade plus.

    Mind you this code uses both String.repeat and String.trimEnd, which do not exist in Internet Explorer. There are many sources that polyfill these routines, including those provided on the MDN pages for them:

    https://developer.mozilla.org/en-US/...String/trimEnd

    https://developer.mozilla.org/en-US/.../String/repeat

    My own elementals.js library also provides polyfills for legacy IE.

    elementals.js

    The two 'write' functions in this standalone code being subsets of elementals.js' more robust '_.make' method.

    Alright, I'm probably butting heads with the post size limit, but since I magically have a day off I'll write up an explanation of the how/what/why of that scripting so you can learn from it.
    “There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies and the other way is to make it so complicated that there are no obvious deficiencies.” – C.A.R. Hoare, The 1980 ACM Turing Award Lecture
    http://www.cutcodedown.com

  4. #4
    Senior Coder deathshadow's Avatar
    Join Date
    Feb 2016
    Location
    Keene, NH
    Posts
    3,222
    Thanks
    4
    Thanked 470 Times in 458 Posts
    So let's break down this code... Let's start with the markup, follow along with this .txt if you don't know how to 'view source':

    JavaScript Stylesheet Display

    Notice first off that I condense the DOCTYPE through character encoding meta to a single line. This is NOT done so as to save a few bytes, but as a gentle reminder that these tags MUST (or at least should) appear IN THIS ORDER and that you should not paste anything in-between them. I do the same for </head><body> and </body></html>. It's just a good habit to get into and a nice reminder NOT to screw with what's in/around those. Lost track of the number of beginners I've seen put things between </head> and <body> where it's completely invalid to have anything but whitespace!

    I don't say type="text/css" or type="text/javascript" anymore as they were never actually necessary for the browsers, and were dropped as required when rel="stylesheet" or on <script> tags (respectively) in HTML 5.

    I do however give the <link> a media target, since sending screen layout to print, aural, or braille is pretty damned stupid. One of the many reasons style="" is mentally enfeebled trash except for the rare corner-cases where style conveys meaning. Just say no to style=""!

    Whilst I included an inline style for testing, it is NOT something I would suggest doing in production code. Presentation has ZERO damned business in your markup, and it ends up being a missed caching opportunity when the same styles are used across multiple pages -- or when a user revisits the same page on a content change -- and can also be a missed opportunity to pre-cache the appearance of sub-pages.

    Whoever is spreading the LIE that moving style into the markup is going to make things like render 'faster' is begging for my size 9 to be shoved so far up their backside it will take an orthodontist to handle the extraction!

    The H1 is the main heading, the output DIV you already know about, but notice the noscript! Always provide feedback for scripting only functionality instead of leaving the user hanging without a clue!

    Also I put the script external so it too can be cached separate from the content, since scripting off/blocked there's no reason to waste time having it in the markup!

    ... so nothing to write home about on the HTML front. Now, for the JS. Follow the bouncing ball at:

    http://www.cutcodedown.com/for_other...tyleDisplay.js

    First we open our SIF so this script is hidden from other scripts and we can pass 'document' as 'd'. I store div#output in the 'output' variable so that we aren't constantly diving for the document to 'get' it. getElementById is handy, but slow, so avoid doing that when you don't have to.

    The [i]writeTag[/b] function takes the tag name and text content as its parameters. The tagName is used to createElement said tag as 'e', whilst the content is appended to that element as a textNode. document.createTextNode automatically sanitizes to plaintext anything it outputs, making code injection impossible. Finally the created element 'e' is appended to the output area. Boom, tag with content, no innerHTML meaning no security risks and it most likely runs faster too!

    writeLine follows a similar pattern in that it appends to output the contents of our 'buffer' with utf-8 non-breaking spaces based on our indent depth. A break tag is then created and appended. Finally it empties the buffer.

    I added a check for document.stylesheets.length since feedback and error handling for if there are no stylesheets is important. We then loop through all the sheets showing the appropriate depth heading to say which sheet we're outputting starting each sub-section. Then looping through each of the rulesets we output the next depth of heading to say which index is being handled, then we loop each 'utf safe' character of that cssText.

    Since the } does not belong on the same line and does the de-indent we need to handle that first. If the buffer is full we write everything in it flushing its contents. We then decrease the indent, set the buffer to the }, and write once again.

    Otherwise we will want to add the current character to the buffer regardless of what it is. When checking the value of one variable more than once in the same scope/level rather than if/else you should use switch/case as it is more efficient at that. If it's a { we write buffer+break and increase the indent AFTER. If it's a semi-colon we just write the buffer+break.

    Finally AFTER the loop we want to make sure we flush the buffer to our output.

    Close, close, close, output our error message if there's no stylesheets to process, then end our SIF passing document as 'd'.

    ... and that's how it probably SHOULD be done. Sounds like that video you're following is rubbish -- but then I can't learn a damned thing from videos. My brain is text oriented and just doesn't "work that way" when it comes to learning.
    “There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies and the other way is to make it so complicated that there are no obvious deficiencies.” – C.A.R. Hoare, The 1980 ACM Turing Award Lecture
    http://www.cutcodedown.com

  5. #5
    Senior Coder deathshadow's Avatar
    Join Date
    Feb 2016
    Location
    Keene, NH
    Posts
    3,222
    Thanks
    4
    Thanked 470 Times in 458 Posts
    Oh, and it MAY be even faster to use indexOf with substr to parse out the {, }, and ; instead of going one character at a time, but I tried to retain the intent of the technique you were using/learning since per-character parsing is important to know how to do as well.
    “There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies and the other way is to make it so complicated that there are no obvious deficiencies.” – C.A.R. Hoare, The 1980 ACM Turing Award Lecture
    http://www.cutcodedown.com


 

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •