Javascript in theme component only runs once?


#1

Hi there, I’ve got a javascript snippet that I’m supposed to put before the closing body tag. It works, but just as described on this very old topic, it only seems to run on first page load. Because it is supposed to scan the contents of posts and create tooltips and links for certain phrases, it needs to run each time new content is loaded.

Because of the huge combination of potential strings (any Bible passage reference) to be linked, it is impossible to use a solution such as the Linkify words in post plugin.

Apparently this sort of thing is possible to pull off in a theme component, as linkify does, but in my case the snippet just loads the actual javascript file from an external server and tells it to run. Anybody have any idea how I can get this working on content loaded via ajax?

Here’s the javascript snippet:

    <script>
	var refTagger = {
		settings: {
			bibleVersion: "NASB",			
			socialSharing: []
		}
	};
	(function(d, t) {
		var g = d.createElement(t), s = d.getElementsByTagName(t)[0];
		g.src = "//api.reftagger.com/v2/RefTagger.js";
		s.parentNode.insertBefore(g, s);
	}(document, "script"));
    </script>

(Rafael dos Santos Silva) #2

Use the page tracker API to call the necessary function on page change. Here an example:


#3

Thanks. That got me on the right track. However, in looking at How do you force a script to refire on every page load in Discourse?, and the code of the linkify component, I believe I need to use the api.decorateCooked rather than api.onPageChange. Does that sound right?

Problem is, I can’t figure out how to use either one. I was able to get it “working” without the API, but like the above mentioned issue, it fires 3 times on page load. Here’s the code that works:

<script>
	var refTagger = {
		settings: {
			bibleVersion: "NASB",			
			socialSharing: []
		}
	};
	(function(d, t) {
		var g = d.createElement(t), s = d.getElementsByTagName(t)[0];
		g.src = "//api.reftagger.com/v2/RefTagger.js";
		s.parentNode.insertBefore(g, s);
	}(document, "script"));

$(document).ajaxComplete(function() {
    refTagger.tag();
});

</script>

If I replace the ajaxComplete section with the following, there’s an error in the console: “ReferenceError: Can’t find variable: api”.

  api.decorateCooked($elem => {
    refTagger.tag();
  });

If I change the opening script tag as follows, the error changes to "“refTagger.tag is not a function. (In ‘refTagger.tag()’, ‘refTagger.tag’ is undefined)”

<script type="text/discourse-plugin" version="0.2">

I don’t know javascript, so I’m just lost now. I’d like to do this the “proper” way if anybody can help me fix it. Thanks much.

By the way, it’s also possible apparently to run refTagger on specific content (rather than the whole page, I guess). This is some example code I found:

var o_c = document.getElementById('comment');

o_c.innerHTML = "See [Romans 1:10](http://biblia.com/bible/esv/Rom%201.10) for details";

refTagger.tag(o_c);

Not sure if it would be possible to just do something like the following, which is similar to what linkify does. If so, it seems like this would be most efficient:

  api.decorateCooked($elem => {
    refTagger($elem[0]);
  });

Anyway, as I said, if anybody can help, I’d be grateful.


(David Taylor) #4

To use the api you will need to put the code inside a discourse-plugin block. (It’s still javascript, just with a sprinkling of Discourse added on top).

So something like:

<script type="text/discourse-plugin" version="0.2">
api.decorateCooked($elem => {
    refTagger.tag($elem[0]);
  });
</script>

#5

I think you missed it, I already tried that exact code. It then tells me “refTagger.tag is not a function. (In ‘refTagger.tag($elem[0])’, ‘refTagger.tag’ is undefined)”

I feel like I’m just missing something basic about how to declare or call these scripts.


(Rafael dos Santos Silva) #6

That means the script isn’t available. This isn’t really a Discourse question, but:

<script src="https://api.reftagger.com/v2/RefTagger.js"></script>

<script type="text/discourse-plugin" version="0.2">
  api.decorateCooked($elem => {
    refTagger.tag($elem[0]);
  });
</script>

should work.


(David Taylor) #7

Looks like refTagger requires a variable to be set on the window object before the script is loaded, which isn’t possible by using multiple script tags (any inline scripts are extracted and executed at the end). This method works - using our loadScript function:

<script type="text/discourse-plugin" version="0.2">
window.refTagger = {
	settings: {
		bibleVersion: "NASB",			
		socialSharing: []
	}
};

let loadScript = require('discourse/lib/load-script').default;

api.decorateCooked($elem => {
    loadScript("//api.reftagger.com/v2/RefTagger.js").then(()=>{
        window.refTagger.tag($elem[0]);    
    })
});
</script>


#9

Thanks! It would have taken me a while to figure that out, not knowing javascript, much less the intricacies of Discourse. :slight_smile:

It’s working like a charm.

I appreciate all the help you guys give.