Custom CSS feature to inject custom JavaScript


  • Plugin & Theme Dev

    There is a couple ways to inject JavaScript in NodeBB pages (MOTD on the home page, Widgets on certain pages, and newly introduced the Composer Help), but I didn't find an easy way to do that on EVERY page (aka header or footer), without creating a custom plugin and changing its source, like this shitty one i created which doesn't work correctly without re-writing a file to the file system, until I thought of this:

    Admin > Themes > Custom CSS

    close the </style> tag, place your html, I chose a <script> tag, then reopen the <style>
    Screen Shot 2014-03-02 at 10.50.39 AM.png

    generates this

    Screen Shot 2014-03-02 at 10.50.15 AM.png

    @psychobunny please don't close that on us 😞 it's not a security risk, unless the admin deliberately inject a malicious script


  • Admin

    /banstick

    Just kidding Aziz, haha... this is really interesting (if a little hacky)!

    Keep in mind @psychobunny is actively working on bringing "global" header, footer, and sidebars to NodeBB. I believe all that it really takes is updating a theme, but I may be mistaken. Once that is in, then adding custom javascript should be quite easy (and effectively deprecate plugin-42)

    Edit: The problem is that adding a script to a widget causes that script to be invoked every time that page is ajaxified into (plus perhaps some people want to only invoke a script once on page load, etc)... so the global footer widget space (when implemented) should resolve that. My GA plugin could definitely benefit from this.



  • @julian

    The problem is that adding a script to a widget causes that script to be invoked every time that page is ajaxified into (plus perhaps some people want to only invoke a script once on page load, etc)

    From a commercial publisher's perspective, having a script (usually an ad script) invoked every time a page is ajaxified might not be such a bad thing, that is, if we equate every time a page is ajaxified to a page load.


  • Plugin & Theme Dev

    @julianlam actually no, this script executes only once, I did some testing on most of the pages I could think of, minus the /admin routes, which doesn't apply there.

    @planner is you want it to execute for every page, use the action:ajaxifying or action:ajaxifed or action:page.load hook, not sure which lol

    <script>
    $(function(){
    	$('body').on('action:ajaxifying', function(e, data) {
    		/// your script here
    	});
    });
    </script>
    

  • Admin

    @bentael said:

    @julianlam actually no, this script executes only once, I did some testing on most of the pages I could think of, minus the /admin routes, which doesn't apply there.

    Yeah, you're right, I meant adding scripts using the existing widget boxes. Your workaround does work as advertised 🙂

    action:ajaxifying, action:ajaxified, and action:page.load all execute once, just at different parts of the "page loading" sequence of events 😄


  • Admin

    @bentael

    @psychobunny please don't close that on us frowning it's not a security risk, unless the admin deliberately inject a malicious script

    😆

    yeah, global widget areas coming soon. nice trick though 😛



  • @bentael

    I put a GA tag in the code to give me something like this, but page load hangs at about 80%. What could be wrong?

    <script>
    $(function(){
        $('body').on('action:ajaxifying', function(e, data) {
            /// your script here
    <script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
    <!-- Forum Header -->
    <ins class="adsbygoogle"
         style="display:block"
         data-ad-client="ca-pub-8aa076493813069"
         data-ad-slot="8248656285"
         data-ad-format="auto"></ins>
    <script>
    (adsbygoogle = window.adsbygoogle || []).push({});
    </script>
        });
    });
    </script>
    

  • Plugin & Theme Dev

    @planner your markup/syntax is invalid.
    If I got this right, you want to inject a script into the header, and make a call every time a page is loaded, so I think here's how it should look like:

    Note this snippet is to be used on the Custom CSS page in the Admin console.,

    /* at this point we're in a <style> tag. so let me close that tag for you */
    </style>
    
    <!-- here you can place regular html, this is a comment, you can remove all the comments if you want -->
    
    <!-- so I am going to inject the googlesyndycation script  only one time on the page -->
    <script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
    
    <!-- and I am going to place the placeholder element for it, also one time on the page -->
    <ins class="adsbygoogle" style="display:block" data-ad-client="ca-pub-8aa076493813069" data-ad-slot="8248656285" data-ad-format="auto"></ins>
    
    <!-- then, on document.ready, I am going to attach an event listener to the body, so it would trigger the 'adsbygoogle.push()' call, everytime there is a page ajaxifying -->
    <script>
    $(function(){
        $('body').on('action:ajaxifying', function(e, data) {
                (adsbygoogle = window.adsbygoogle || []).push({});
        });
    });
    </script>
    
    <!-- then, I am going to reopen that <style> tag so the end result html injected by NodeBB would still be valid -->
    <style>
    /* now we're back inside of a style tag so we use a different syntax for commenting if you noticed .. */
    
    

    However, please note that action:axaxifying may not be the correct event to listen to, I will let @julian correct that if it's not right.
    I hope that answers your question


  • Plugin & Theme Dev

    also note that this tag,

    <ins class="adsbygoogle" style="display:block" data-ad-client="ca-pub-8aa076493813069" data-ad-slot="8248656285" data-ad-format="auto"></ins>
    

    doesn't have to be here, in fact, you most likely don't want it to be here, it could live in a widget or whereever... another template.. footer ..



  • @bentael

    Thanks, I'll take a closer look at that code. However, that's just one of the things I'm tying to do. The other is add analytics tag from Piwik and Quantcast to the footer, so that all user page actions, whether Ajaxified or not, register as unique page views.

    For example, I have this Piwik tag that needs to go in a Global footer widget. The problem is it does not register all user activities:

    <script type="text/javascript">
      var _paq = _paq || [];
      _paq.push(["setDocumentTitle", document.domain + "/" + document.title]);
      _paq.push(["setCookieDomain", "*.linuxisi.com"]);
      _paq.push(["trackPageView"]);
      _paq.push(["enableLinkTracking"]);
    
      (function() {
        var u=(("https:" == document.location.protocol) ? "https" : "http") + "://linise.com/analyz/";
        _paq.push(["setTrackerUrl", u+"piwik.php"]);
        _paq.push(["setSiteId", "1"]);
        var d=document, g=d.createElement("script"), s=d.getElementsByTagName("script")[0]; g.type="text/javascript";
        g.defer=true; g.async=true; g.src=u+"piwik.js"; s.parentNode.insertBefore(g,s);
      })();
    </script>
    

    Even when I enclose insert it in the code you gave some time ago, it still does not seem to trigger on all user activities.

    This is the bit of code you provided. I know it's the same you used in your post, but when it comes to splitting JS tag like you did with the Google tag, I can very easily screw things up. I cam mess with HTML/CSS, but not JS. That should change by the end of the year, though 😁

    <script>
    $(function(){
        $('body').on('action:ajaxifying', function(e, data) {
            /// your script here
    	
        });
    });
    </script>
    

  • Plugin & Theme Dev

    ok so, it turns out $('body').on('action:ajaxifying') no longer works, I think it got replaced by action:ajaxify.end and it's on the window instead of the body. However, don't take my word for it yet, track that thread.

    Assuming, i am right, you should be able to do that

    edit: @planner That should work

    </style>
    
    <!-- gooogle ads -->
    <script async defer src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
    <ins class="adsbygoogle" style="display:block" data-ad-client="ca-pub-8aa076493813069" data-ad-slot="8248656285" data-ad-format="auto"></ins>
    
    <script>
       // global _paq
        var _paq = _paq || [];
        _paq.push(["setCookieDomain", "*.linuxisi.com"]);
        _paq.push(["enableLinkTracking"]);
        _paq.push(["setTrackerUrl", ("https:" == document.location.protocol) ? "https" : "http") + "://linise.com/analyz/piwik.php"]);
        _paq.push(["setSiteId", "1"]);
    </script>
    
    <!-- then inject the piwic script, the old fashioned way, instead of writing the script tag -->
    <script async defer src="//linise.com/analyz/piwik.js"></script>
    
    <script>
    $(function() {
        $(window).on('action:ajaxify.end', function(e, data) {
                (adsbygoogle = window.adsbygoogle || []).push({});
    
                 // according to the piwik documentation, http://developer.piwik.org/api-reference/tracking-javascript, i should be to trigger page view like that
                _paq.push(["setDocumentTitle", document.domain + "/" + document.title]); // I set the document title every time, because it may have changed
                _paq.push(["trackPageView"]);
        });
    });
    </script>
    <style>
    

    You do not have to have all of your javascript in the footer, it's true that it helps a teeny tiny bit with page performance, since scripts execution is usually synchronous, (but using the html5 async and defer remedies that a bit for new browsers) anyways trust me, you will never feel the difference for small scripts like these.



  • @bentael

    Thanks, I'll try it out later today



  • @julian so how is it going with the work of @psychobunny ? It would be an extremely useful feature.


  • Admin

    @DennisSun Widget system is in NodeBB, starting v0.4.1 (or maybe v0.4.2)... 🙂



  • @julian Hi, I can see widget system in the dashboard, but there is no "javascript widget" within it. My version is v0.4.3.


  • GNU/Linux

    @DennisSun The widget is called "HTML", just add a javascript-tag 😉



  • @frissdiegurke well , I tried to involve javascript code in the html widget. But it didn't work. I can't even see the whole homepage after that.


  • GNU/Linux

    @DennisSun Any errors on console?

    this example works fine for me:

    <script type="text/javascript">
      console.log('hello world!');
    </script>
    


  • @frissdiegurke no error showed in log.

    here is what I added:
    <script type="text/javascript">
    var _bdhmProtocol = (("https:" == document.location.protocol) ? " https://" : " http://");
    document.write(unescape("%3Cscript src='" + _bdhmProtocol + "hm.baidu.com/h.js%3F0da233db09b6a0886cd3f9d1a52e64d8' type='text/javascript'%3E%3C/script%3E"));
    </script>

    The analytics code form baidu.com


  • GNU/Linux

    yes, document.write replaces the whole site 😉 use sth like

    <script src='https://hm.baidu.com/h.js?0da233db09b6a0886cd3f9d1a52e64d8' type='text/javascript'></script>
    

    this seems to be what the script is supposed to do (or http:// if you don't use TLS).


Log in to reply
 


Looks like your connection to NodeBB was lost, please wait while we try to reconnect.