Custom CSS feature to inject custom JavaScript
-
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>
generates this
@psychobunny please don't close that on us it's not a security risk, unless the admin deliberately inject a malicious script
-
/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.
-
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.
-
@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
oraction:ajaxifed
oraction:page.load
hook, not sure which lol<script> $(function(){ $('body').on('action:ajaxifying', function(e, data) { /// your script here }); }); </script>
-
@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
, andaction:page.load
all execute once, just at different parts of the "page loading" sequence of events -
@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
-
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>
-
@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 -
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 ..
-
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>
-
ok so, it turns out
$('body').on('action:ajaxifying')
no longer works, I think it got replaced byaction:ajaxify.end
and it's on thewindow
instead of thebody
.However, don't take my word for it yet, track that thread.Assuming, i am right, you should be able to do thatedit: @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
anddefer
remedies that a bit for new browsers) anyways trust me, you will never feel the difference for small scripts like these. -
@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.
-
@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
-
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).