How to load error trackers from script tag? - javascript

I'm working on small .js which is going to be embedded on multiple websites, it will be loaded in a classic way - via script tag: <script src="myscript.js"></script> in sites body tag. I cannot add any more scripts to those sites.
I would like to track errors with error tracker such as Sentry, Rollup or HoneyBadger. However, all of them require being loaded with another script tag, most preferred before everything else.
Note: Those services need to load before everything else to catch errors property.
As I cannot add another script tag in the site's code, I need to execute their code inside my script, but before my actual script code.
I tried taking the content of HoneyBadger javascript library and putting it directly inside my file - it worked, however, I feel like it's terrible practice, as their code is written with modern browsers in mind, and mine supports older ones.
Is there any good way in my situation to load their .js externally?

I don't think that would work because of the way honeybadger.js v0.5 parses the script tag to get those attributes--it looks for the script tag in the dom when it's loaded.
Also, we've moved away from using the data- attributes in honeybadger.js v1.0, which was just released. In that version, you must use Honeybadger.configure to set your API key. Take a look at the new docs here:
https://docs.honeybadger.io/lib/javascript/integration/browser.html
I'd recommend going with v1.0, and using Honeybadger.configure for the configuration.

Related

Why do websites like Hotjar and Google Analytics use complex tracking code instead of just a <script> tag?

Website that use JS tracking usually use this kind of code :
<script>
(function(h,o,t,j,a,r){
h.hj=h.hj||function(){(h.hj.q=h.hj.q||[]).push(arguments)};
h._hjSettings={hjid:9999,hjsv:5};
a=o.getElementsByTagName('head')[0];
r=o.createElement('script');r.async=1;
r.src=t+h._hjSettings.hjid+j+h._hjSettings.hjsv;
a.appendChild(r);
})(window,document,'//static.hotjar.com/c/hotjar-','.js?sv=');
</script>
In the end, those scripts just add a <script> tag to the <head> of the page, so surely there must be a reason why they're doing it this way.
Is it for ad-blocking bypass reasons ? Wouldn't the generated request be the same as if it was hardcoded in the <head> ?
I'm the chief architect at Hotjar so I'll explain the reasons why we did it in this particular way.
We need to do things before the main script is loaded.
h.hj=h.hj||function(){(h.hj.q=h.hj.q||[]).push(arguments)};
That particular line allows us to store actions to execute once the main script is loaded. It allows for things like hj('trackVirtualPageView', '/url') to be called before our script is loaded.
We can store things like settings as part of the snippet.
h._hjSettings={hjid:9999,hjsv:5};
That could absolutely be added as part of the query string when loading the script. The downside of using that approach is that we would get less optimal caching since it would be impossible for a browser to know that script.js?hjid=1 and script.js?hjid=2 actually loads the same JS file.
What we're doing in the last part is actually just creating a <script async=1> tag and adding it to the <head> which works really well. The reason we're doing it through JS is that we like to make it as easy as possible for our users by only asking them to put code in one place.
There might be an even better to do what we're doing which I'm blissfully unaware of, and in case there is, please reach out and tell me about it! :)
At least part of the answer is that vendors want to load their libraries in a way that does not block page rendering.
If the browser hits a script element it tries to get the script source, and might prevent the page from rendering until the complete script is downloaded. In the bad old days it used to happen that website would show up blank, because the (then synchronous) Google Analytics script could not be downloaded in a timely fashion and stopped the page from rendering. Script injection became an accepted method to make scripts non-blocking.
There are other ways (defer, asynch, etc - for historical interest here is a link to an 2009 article that discusses the issue, because the problem is that old), but script injection is a convenient way to set up a few variables along the way (plus if Google does it it must be the best way, or so seems to be the though process with some companies).

using document.write in remotely loaded javascript to write out content - why a bad idea?

I'm not a full-time Javascript developer. We have a web app and one piece is to write out a small informational widget onto another domain. This literally is just a html table with some values written out into it. I have had to do this a couple of times over the past 8 years and I always end up doing it via a script that just document.write's out the table.
For example:
document.write('<table border="1"><tr><td>here is some content</td></tr></table>');
on theirdomain.com
<body>
....
<script src='http://ourdomain.com/arc/v1/api/inventory/1' type='text/javascript'></script>
.....
</body>
I always think this is a bit ugly but it works fine and we always have control over the content (or a trusted representative has control such as like your current inventory or something). So another project like this came up and I coded it up in like 5 minutes using document.write. Somebody else thinks this is just too ugly but I don't see what the problem is. Re the widget aspect, I have also done iframe and jsonp implementations but iframe tends not to play well with other site's css and jsonp tends to just be too much. Is there a some security element I'm missing? Or is what I'm doing ok? What would be the strongest argument against using this technique? Is there a best practice I don't get?
To be honest, I don't really see a problem. Yes, document.write is very old-school, but it is simple and universally supported; you can depend on it working the same in every browser.
For your application (writing out a HTML table with some data), I don't think a more complex solution is necessary if you're willing to assume a few small risks. Dealing with DOM mutation that works correctly across browsers is not an easy thing to get right if you're not using jQuery (et al).
The risks of document.write:
Your script must be loaded synchronously. This means a normal inline script tag (like you're already using). However, if someone gets clever and adds the async or defer attributes to your script tag (or does something fancy like appending a dynamically created script element to the head), your script will be loaded asynchronously.
This means that when your script eventually loads and calls write, the main document may have already finished loading and the document is "closed". Calling write on a closed document implicitly calls open, which completely clears the DOM – it's esentially the same as wiping the page clean and starting from scratch. You don't want that.
Because your script is loaded synchronously, you put third-party pages at the mercy of your server. If your server goes down or gets overloaded and responds slowly, every page that contain your script tag cannot finish loading until your server does respond or the browser times out the request.
The people who put your widget on their website will not be happy.
If you're confident in your uptime, then there's really no reason to change what you're doing.
The alternative is to load your script asynchronously and insert your table into the correct spot in the DOM. This means third parties would have to both insert a script snippet (either <script async src="..."> or use the dynamic script tag insertion trick. They would also need to carve out a special <div id="tablegoeshere"> for you to put your table into.
Using document.write() after loading the entire DOM do not allow you to access DOM any further.
See Why do I need to use document.write instead of DOM manipulation methods?.
You are in that case putting away a very powerfull functionnality of in web page...
Is there a some security element I'm missing?
The security risk is for them in that theirdomain.com trusting your domain's script code to not do anthing malicous. Your client script will run in the context of their domain and can do what it likes such as stealing cookies or embedding a key logger (not that you would do that of course). As long as they trust you, that is fine.

Is there a danger to creating a js file that simply uses document.write() to bundle js and css files?

I am building a front-end UI framework for developers in my firm to use to build internal web apps. It consists of customized Bootstrap, jQuery, other open-source libraries, internal modules and stylesheets. The user environment is entirely IE9 and my server is .NET 3.5. I am hosting the shared files. Dev teams in the firm will place the links in their project pages and apply the framework to their pages.
I want to offer them the simplest method of implementing this which would be one line of code to paste that builds the library for them. Cutting and pasting 30 lines of code is stale the moment Ctrl + V is pressed, it leaves me no control and is simply inelegant.
Failed Experiments
I tried using Head.js and LazyLoad both of which use best practices for inserting scripts. But each of them has caused either content to display before styled or conditions where methods are called before scripts load. I am giving up on this approach.
It's too volatile.
A simple document.write() solution
Over the weekend, I thought: Why don't I just make a js file named "framework,js", add the script and link files in order with a stack of document.write() lines. Tell developers to put it in the head and that's it. Heck I could add the necessary metatags for IE9 and mobile too for that matter. It's so nasty and simple... but it just might work!
The user base is on an internal network and of limited size. Bandwidth is not a problem. I'll test performance before I choose this. I can direct the developer teams on where to place the link.
Knowing this and providing it actually works, is there any reason why I shouldn't do this?
My only other option to explore is bundling on the server. I am hoping not to have to resort to this since I don't own the server myself and I am not a .NET developer.
Your proposed approach is an excellent one for your situation. It has several advantages over the more sophisticated solutions, including utter simplicity, completely predictable order of execution, and full compatibility with scripts that may not lend themselves to asynchronous loading. I have used it many times in production applications, as have many other developers.
Of course the other solutions have their advantages too, but for what you're doing there is nothing wrong with good old document.write().
It sounds like you have a number of scripts and stylesheets and are probably loading them from a common directory (or a common root directory). To reduce repetition in your framework.js file, you might want to define two functions, one to write a <link> tag for CSS and another to write a <script> tag for JavaScript. So a skeleton framework.js file might look something like this:
(function() {
var scriptBase = 'js/';
var styleBase = 'css/';
function writeStyle( name ) {
document.write(
'<link rel="stylesheet" href="', styleBase, name, '">',
'</link>'
);
}
function writeScript( name ) {
document.write(
'<script src="', scriptBase, name, '">',
'</script>'
);
}
writeStyle( 'one.css' );
writeStyle( 'two.css' );
writeScript( 'one.js' );
writeScript( 'two.js' );
})();
Note that you don't have to do any special escaping of the </script> text as you may see in code that uses document.write(). That's only necessary when you're putting this code directly inside a <script> tag within the HTML file. The purpose of that escaping is to prevent the enclosing <script> tag from being closed by the </script> text inside the document.write() call. Since your code is in an external .js file this is not an issue: the presence of the </script> text inside this file won't terminate the .js file.
Another point to keep in mind is that all of the document.write() calls you make inside a .js file are inserted into the document after the .js file that writes them. So don't expect to be able to do a document.write() inside framework.js and then have other code inside framework.js that depends on the .js file you just wrote. All of those .js files (and .css) are loaded after framework.js, not interleaved with it.
One more consideration, of course, is load time. Your page could load faster if you combine and minify your CSS and JS files. But if these are internal web apps, page load time may be the least of your worries: reliability and maintainability may be more important. And in any case you can always use the document.write() solution to get up and running right now, and later optimize this only in the unlikely event that you need to.

Wordpress and javascript .load() function

I have problem to get .load() function working in Wordpress. Initially I was using 3.0.5 version of WP, wanted to get some content from external page (same domain), so I used this code
jQuery.noConflict();
jQuery(document).ready(function(){
jQuery(".someclass").load("http://www.mydomain.com #someid");
});
...and it worked. However, after update of Wordpress to latest version (and installation of plugins /some use jquery or mootools/, this piece of code isnt pulling any content anymore. I tried to write different code for noConflict mode but also without success (but JS is working if I change line to some alert func). I also deactivated all plugins, removed other js (like for menu), but still no content was displayed. If I use same code in a separate file (in the same directory where WP theme is) - it works.
I would be thankful if someone have advice what to try next or where to look for potential problem. Or maybe to suggest some other approach how to get content from external page (and specific div). If I put that separate file into iframe and call it within sidebar, it's working but then there's a problem of iframe links opening within iframe box.
Your problem is the same origin policy, which in lamens terms means you can't do ajax requests to different domains (even subdomains) as it is security risk, you browser simply won't let you do it. Specifically in your case you are attempting to load www.infostar.rs from inforstar.rs.
You will need to come up with another idea, personally I would just do it in PHP with:
echo file_get_contents('http://domain.com');
Alternatively would could look into forcing non-www in htaccess.

Loading an external script from within firefox extension

I am currently rewriting a firefox extension to be used only internally at my company. I started out by moving most of the logic into an external js file loaded via a script tag within the xul overlay. (We've found it hard to get our employees to consistently upgrade so I thought I could get around that). It worked fine like this:
<overlay id="my-overlay"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml">
<script type="application/x-javascript" src="https://my.company.com/path/to/jquery.min.js/>
<script type="application/x-javascript" src="https://my.company.com/path/to/toolbar/main.js"/>
<toolbox id="navigator-toolbox">
<!-- some stuff here -->
</toolbox>
But then I had the crazy idea of loading the script file dynamically so that I could use preferences to determine whether it would load from the production servers or alpha/beta servers. And that's when I failed miserably.
I've tried the following unsuccessfully:
$.ajax({ dataType: 'script', ... }) // appears to do absolutely nothing
$('overlay').appendChild('', { src: ... }) // script tag is added but not executed
document.createElementNS, etc // script tag is is added but not executed
Components.utils.import // does not accept http protocol
mozIJSSubScriptLoader // does not accept http protocol
I haven't attempted Components.utils.evalInSandbox but given its restrictions, I'm afraid it would require significant other code changes that would not be worth the slight simplification of the development cycle.
From reading thru much more mozilla documentation and bugs it appears that basically what I'm attempting to do is breaking various security concerns (I understand in principle but of course in my case, I have full control of both ends).
The thing that bothers me is that it appears to work fine as long as I hard code the script tag but only fails once I try to dynamically generate it. Should I just toss in the towel or does someone know a way to make this work?
Have you tried the brute-force approach of reading in the file using XMLHttpRequest and then just calling eval() to run it? On the face of it this seems scary from a security perspective but, as you say, using a script tag with an HTTP source URL is basically the same thing.

Categories