5

Possible Duplicate:
How may I reference the script tag that loaded the currently-executing script?

Is there a way to select the script element that included a particular script without giving it a known attribute of any sort?

This example will alert the window object because it's being called in the global context:

<script>
  alert(this);
</script>

This example wont work as the name of the script could change (for convenience i'm assuming jQuery is included):

<script type="text/javascript" src="/foo/bar/baz.js"></script>
//baz.js
$('[src^="baz.js"]');

This example is sub-optimal as it relies on the id attribute:

<script type="text/javascript" id="foo">
  $('#foo');
</script>
Community
  • 1
  • 1
zzzzBov
  • 174,988
  • 54
  • 320
  • 367
  • you can get all the script tags with `$('script')` but if you don't want to compare to a `SRC` or `ID` then what do you propose to compare? the only thing left is the full source.. – Greg Guida Jul 21 '11 at 16:40
  • 1
    @Greg Guida, I'm not proposing anything here. All I want is to select the script element containing code (local or referenced) that may or may not have been loaded on page load, and which may or may not have consistent attributes. I have fallbacks in mind if it's impossible, but I didn't want to be missing some hidden feature that someone might know about (would be cool if `arguments` could be used in global context.) – zzzzBov Jul 21 '11 at 16:45
  • oh, I see what your getting at now. I think the most elegant way of doing this would have to be server side adding `$('[src^="baz.js"]');` for included scripts and `ID` for inline. I get the feeling though, that you want to do this with pure JavaScript =/. If you don't mind me asking what are you trying to do with the script element once you get it? – Greg Guida Jul 21 '11 at 18:27
  • @Greg Guida, i had a module pattern that I don't mind sharing: for modules being added to a page (i.e. lightbox), it would be convenient if you could add a couple `classes` or `data-attributes` to the `script` element to specify defaults or default actions. It would be *more* convenient if the `script` element could be selected without needing to *also* add a specific `id` or `class`. – zzzzBov Jul 21 '11 at 18:37

3 Answers3

2

I'm hesitant to answer my own question in this case because I don't have an answer that works 100% of the time, but I've ironed out the bugs enough to have a stable solution.

@Shef's answer is halfway to what I wanted/needed.

Generic JS Version:

function getActiveScript()
{
  var s;
  s=document.getElementsByTagName('script');
  return s[s.length - 1];
}

The issue with the above code is that it will select the last script on the page no matter when it was executed. This is a major issue if the calling script was included after page load.

As I can't directly select the script element (unless another solution presents itself), I'd rather not select anything if the script is being added after page load.

Pure JS Answer:

function getActiveScript()
{
  var r,s;
  r = document.readyState;
  if ( r && r != 'complete' )
  {
    s = document.getElementsByTagName('script');
    return s.length ? s[s.length - 1] : null;
  }
  return;
}

In the new version, document.readyState is checked to make sure that the document hasn't finished loading. Additionally on browsers where document.readyState isn't used, it will fail out (I can't think of any browsers that don't use document.readyState these days).

One caveat to this solution is that it may be possible to dynamically insert a script element into an earlier part of the document, which then gets executed before document.readyState is 'complete'. If the script were to use this code, it could possibly be referencing the wrong script. I haven't checked this issue yet. Typically I add new script tags with document.body.appendChild (or equivalent in a library), so it's not that big an issue to me.

Another possible loophole is if a new script has been appended to document.body before getActiveScript has been called. Again, I haven't tested it, but it may mean that the last selected script evaluates to on that's different than the one being executed.


Actual Usage:

The question was asked of why I wanted to select the currently evaluating script element. Although "Because I can" would probably be understood, I did have a legitimate reason to want to use this feature/functionality/dirty-nasty-hack.

I created a (couple) jQuery plugin(s) to do what I originally wanted.

jQuery Plugin: jquery.activescript.js

(function($){
  "use strict";
  $.activeScript = function(){
    var r;
    r=document.readyState;
    return r && r != 'complete' ? $('script:last') : $();
  };
})(jQuery);

jQuery Plugin: jquery.activescript.plugin.js

by default works for jQuery UI

(function($){
  "use strict";
  var plugins,d,p,$p;
  d = $.activeScript().data();
  plugins = d.plugins || 'draggable droppable resizable selectable sortable accordion autocomplete button datepicker dialog progressbar slider tabs';
  plugins=plugins.split(' ');
  $(function(){
    while(p=plugins.pop())
    {
      $p=$(d[p+'Selector']);
      if ($p[p])$p[p]();
    }
  });
})(jQuery);

The way you'd use jquery.activescript.plugin.js is as follows:

<script type="text/javascript" src="jquery.activescript.plugin.js"
  data-draggable-selector=".draggable.default"
  data-droppable-selector=".droppable.default"
  data-resizable-selector=".resizable.default"
  data-selectable-selector=".selectable.default"
  data-sortable-selector=".sortable.default"
  data-accordion-selector=".accordion.default"
  data-autocomplete-selector=".autocomplete.default"
  data-button-selector=".button.default"
  data-datepicker-selector=".datepicker.default"
  data-dialog-selector=".dialog.default"
  data-progressbar-selector=".progressbar.default"
  data-slider-selector=".slider.default"
  data-tabs-selector=".tabs.default"></script>

This would allow you to semantically tie in your default plugins to your page without having to muck about in a JS file. If non-defaults are needed, you should muck about in a JS file, so I haven't added any mechanism for specifying options (although a few plugins could really use it).

This same basic activescript mechanism could be used by standalone plugins to have an easier way of overriding defaults.

zzzzBov
  • 174,988
  • 54
  • 320
  • 367
1

Inside your included script:

(function($){
    var $current_script = $('script:last');
})(jQuery);

JavaScript is a top to bottom language, so, the last script tag being executed will be the current one.

Shef
  • 44,808
  • 15
  • 79
  • 90
  • 1
    Unless, of course, you have it wrapped inside a $(document).ready(), no? – Steve Wang Jul 21 '11 at 16:39
  • @Shef, very good idea. One issue with that implementation is a dynamically loaded script might not be the last script on the page. – zzzzBov Jul 21 '11 at 16:40
  • @Steve Wang: Doesn't matter if it is attached to the ready event. It will always run, because by the time the script will be executed, the script element has been loaded. – Shef Jul 21 '11 at 16:59
  • @zzzzBov: Yes, this won't do if the script is loaded dynamically after the page has loaded. – Shef Jul 21 '11 at 16:59
  • @Shef -- zzzzBov's point was what I was getting at. – Steve Wang Jul 21 '11 at 19:04
  • @Steve Wang: Ahh... I see. Well, in that case, that's obvious. However, you should have much more freedom to pass the just included script element around, since you are the one doing the addition to the DOM in the first place. – Shef Jul 21 '11 at 19:06
0

You could scan the innerHTML of the various scripts, although that won't work too well for included scripts.

$('script').filter(function () {
  return this.innerHTML.indexOf('alert(this)')==0;
});

Also, this would require you to figure out some string unique to that particular script. Unless, of course, you like using magic numbers and would rather just directly access, say, the fourth script on the page. Which'll just be $('script')[3], due to 0-indexing.

Steve Wang
  • 1,814
  • 1
  • 16
  • 12