prettyPrint() doesn't get called on page load
Asked Answered
J

2

16

I'm trying to prettify my code in Blogger. I've linked Google JS and CSS files to my template. The problem is that I want the code to be prettified on page load, so I add prettyPrint(); to page load event on the template.

<body onload="prettyPrint();">

This code doesn't get executed. However, if I type prettyPrint() manually in the console, my codes get prettified correctly. Does blogger template block invoking JS functions manually?

EDIT I get it to work by manually invoking the function in every post that I needs code prettifying (see below). Still, I want to know why I can't do it on the template.

<pre class="prettyprint linenums lang-js">
function testCode(){

}
</pre>
// I have to do this in every post :-s
<script type="text/javascript">
prettyPrint();
</script>

EDIT 2 The README said that I shouldn't use prettyPrint() directly as a handler but wrap it in a closure instead. So I added this code, similar to the example in the README, in my <head> but to no avail.

<script type='text/javascript'>
window.addEventListener('load', function (event) { prettyPrint() }, false); 
</script>

OR

<script type='text/javascript'>
document.addEventListener('DOMContentLoaded',function() {
    prettyPrint();
});
</script>

EDIT 3 My template HTML is just the default Dynamics View (Classic) Template with the prettify library added as explained above.

EDIT 4 Here is a link to demonstrate the problem: http://testprettyprint.blogspot.com/2013/02/blog-post.html -- the code block is not prettified automatically but if you open Chrome's console and type prettyPrint() the codes will be correctly highlighted.

EDIT 5 The reason why I think it's my problem not blogger's because this guy still has his code prettified using the same technique: http://errorbuster.blogspot.com/2012/07/prettify-syntax-highlighter-for-blogger.html

EDIT 6 As Jeffery To has pointed in his answer, the Dynamics View loads blog content with AJAX, so any JS call on document load will get executed BEFORE the actual content is loaded. Therefore, any JavaScript performed on the actual blog content, not the document, is invalid. So I guess the question now is how to hook into Dynamics View ajax:complete event, if there is such a thing, but I doubt there is. Thanks everyone who has replied. I'm not sure if this can be counted as a bug, but I'll file an issue with blogger.

CONCLUSION Please read Jeffery To's answer. He found the event to invoke the function.

Juback answered 26/1, 2013 at 18:17 Comment(11)
what argument are you giving to prettyPrint()?Epicurean
Where is it defined, in the "Google JS" you mentioned?Epicurean
This is the JS: google-code-prettify.googlecode.com/svn/trunk/src/prettify.js. And here is the css: google-code-prettify.googlecode.com/svn/trunk/styles/…. They are added to the <head> of my template.Juback
Do you see any error messages in the debug console? If you replace the prettyPrint() call with alert('onload happened') does it pop--up an alert when you expect?Productive
@MikeSamuel Yes it gets alerted alright. It was actually what I did :)Juback
No error messages either? Can you reduce your HTML to a single example and add it to the question or post it to the prettify issue tracker?Productive
@MikeSamuel Thanks. Please see the edit. If you can't spot any abnormality, I guess I'll post it to the issue tracker.Juback
This is kind of tough because I'm unsure whether the input is XHTML/HTML, and how that is pre-processed before being served. If you could curl your page and show the actual HTML arriving on the wire, it might help. One thing I notice though is that the script document.addEventListener(&#39;DOMContentLoaded&#39;,...) looks like XHTML? If Blogger is using XHTML internally for storage but shipping HTML, then it's mangling quotes. Perhaps, if there's some XHTML/HTML confusion, adding a /*<[!CDATA[*/...//]]> section around the script body might help.Productive
Hi, the curl result is HTML. Just want to let you know and say thanks for the diagnosis. I'll try CDATA when I get some time today.Juback
Can you post a link or Fiddle demonstrating the problem?Among
@JefferyTo I've added a link :)Juback
A
18

I believe your test page is using Blogger's "Ajax templates" (not sure what the official name is), whereas the other link you posted is using classic templates.

If you reload your test page, you'll first see the Blogger logo in the middle of the page, then your content appears. If you view the test page source, you'll see a lot of code, but not your content. I believe with layout templates, Blogger's code first runs, then it fetches your content using Ajax. Any JavaScript code that runs on document ready (DOMContentLoaded) or window load will run before your content is loaded into the page.

I believe if you add a HTML/Javascript widget to the page (after your content block), then include a call to prettyPrint() within that widget, it should be called every time a post is displayed.


Update: The official name of this template is "Dynamic Views", and it appears that the HTML / JavaScript widget isn't supported for Dynamic Views. I can't find any mention of an API that lets you hook into Blogger's JavaScript, so I believe the answer (for now at least) is that it's not possible to add template-level code to do what you want. The most practical way would be to include a script tag with prettyPrint() in every post :-(


Update 2: I've traced through Blogger's code and I think I've found a suitable (and bindable) event. Try including this after the Dynamic Views code (in the head element, after the <script src='//www.blogblog.com/dynamicviews/...'></script> tags):

<script>
$(window.blogger.ui()).on('viewitem', function (event, post, element) {
    prettyPrint();
});
</script>

The viewitem event is triggered every time the user views an item (which can happen multiple times in the page's lifetime). The element argument is a jQuery object of the item's container element, so if you want to save prettyPrint() a little time you can do this:

<script>
$(window.blogger.ui()).on('viewitem', function (event, post, element) {
    element.each(function () {
        // first argument is a callback function
        // second argument is a root element or document to check
        prettyPrint(null, this);
    });
});
</script>
Among answered 2/2, 2013 at 7:10 Comment(2)
Thanks. Is there any way to do this on the template? For example, is the anyway to hook into blogger's ajax success callback from the template itself?Juback
Finally I can add whatever custom script I like to a Blogger page. Thank you very much.Monarchism
F
3

There can be a few problems with your snippets above. If you hook an event with <body onload="..."> other javascript codes could overwrite it. Dom.addEventListener will not work with internet explorer, you need to use Dom.attachEvent there. Basicly when I don't or can't use a javascript framework, like jquery for event handling, then I use my old code for onload event handling:

// window.ready
function register_onload(func){
    var oldOnload = window.onload;
    if(typeof(oldOnload) == 'function'){
        window.onload = function(){
            oldOnload();
            func();
        }
    }else{
        window.onload = function(){
            func();
        }
    }
}

// document.ready
function register_onload(func){
    var old_onload = document.onreadystatechange;
    if(typeof old_onload == 'function'){
        document.onreadystatechange = function(){
            old_onload();
            if(document.readyState == 'complete'){
                func();
            }
        }
    }else{
        document.onreadystatechange = function(){
            if(document.readyState == 'complete'){
                func();
            }
        }
    }
}

I attach two codes which work in slightly different ways. Mainly if you use document.ready( 2nd snippet ), then the hooked code will run when the page is loaded and all HTML elements are created. But on document.ready, there is no guarantee, that all javasript and css codes are loaded also, so if you want all codes and scripts to be loaded, then you need window.ready( 1st snippet ).

For your situation, I belive you need the 2nd, document.ready snippet and you can use it the following way:

// copy 2nd snippet here...
register_onload(function(){
    prettyPrint();
});

Also I recommend you put your javacript code between /*<![CDATA[*/ ... /*]]>*/ symbols, so your final code will look like:

<script type="text/javascript">/*<![CDATA[*/
// shorter version of the 2nd snippet
function register_onload(f){
    var d=document,o=d.onreadystatechange;
    if(typeof(o)=='function'){
        d.onreadystatechange=function(){o();if(d.readyState=='complete')f();}
    }else{
        d.onreadystatechange=function(){if(d.readyState=='complete')f();}
    }
}
register_onload(function(){
    prettyPrint();
});
/*]]>*/</script>

Hope this helps you solve your problem.

Flatling answered 1/2, 2013 at 17:17 Comment(1)
Thanks for the detailed answer. I didn't work though :( And I've tried quite a few onload approaches, including jQuery. I've also tried CDATA as a comment suggested. No luck :( I'll replicate the problem and post the link soon...Juback

© 2022 - 2024 — McMap. All rights reserved.