views:

140

answers:

7

I want to give a static javascript block of code to a html template designer, which can be:

  • either inline or external or both
  • used once or more in the html template

and each block can determine its position in the template relative to the other javascript code blocks.

An example could be image banners served using javascript. I give code to template designer who places it in two places, once for a horizontal banner in the header and once for a vertical banner. The same code runs in both blocks but knowing their positions can determine if to serve a horizontal or a vertical image banner.

Make sense?

Another example: Say you have the same 2 javascript tags in a web page calling an external script on a server. Can the server and/or scripts determine which javascript tag it belongs to?

NOTE: Can we say this is a challenge? I know that I can avoid this puzzle very easily but I come across this on a regular basis.

A: 

Why not not just place the function call on the page instead of the entire code block? This way you can pass in a parameter to tell it what type of advertisement is needed?

BuildAd('Tower');

BuildAd('Banner');

Javascript itself has no clue of it's position in a page. You have to target a control on the page to get it's location.

used2could
I need it to be static.
zaf
@zaf: What are you using "static" to mean?
T.J. Crowder
@T.J. Crowder The BuildAd(...) function is not "static" since the parameter changes according to the placement.
zaf
A: 

I don't think it is possible for JavaScript code to know where it was loaded from. It certainly doesn't run at the point it is found, since execution isn't directly tied to the loading process (code usually runs after the whole DOM is loaded). In fact, in the case of externals, it doesn't even make sense, since only one copy of the code will be loaded no matter how many times it is encountered.

Marcelo Cantos
This is not completely true; inline JavaScript runs the moment the browser has parsed the end tag; this is why you must be careful when you access the DOM in them and why you need body.onload.
Aaron Digulla
*"code usually runs after the whole DOM is loaded"* That is not correct unless you specify the `defer` or `async` attribute. If you don't, the HTML parser has to stop and ask the JavaScript interpreter to run the code for the script block as soon as it finds it -- because the script block can emit HTML (via `document.write`). This is why script tags can hold up the rendering of a page. IE introduced the `defer` attribute, and both `defer` and `async` have now been added to HTML5. They both (in different ways) allow the HTML parser/renderer to continue without waiting for the code to execute.
T.J. Crowder
A: 

It shouldn't be the same code for each banner - there will be a parameter passed to whatever is serving the image banner which will specify the intended size.

Can you give a specific example of what you need this for?

To edit for your recent example: The simple answer is no. I could help you approach the problem from a different direction if you post details of your problem

Gausie
+2  A: 

JavaScript code can locate all <script> elements on the page and it can probably examine the attributes and the content to check from which element it came from. But that's probably not what you want.

What you want is a piece of JavaScript which replaces tags on the page with ad banners. The usual solution is to add a special element, say a IMG, for this and give that IMG an id or a class or maybe even a custom attribute (like adtype="vertical") and then use JavaScript to locate these elements and replace the content by changing the src attribute.

For example, using jQuery, you can should your images like so:

<img src="empty.gif" width="..." height="..." class="ad" adtype="..." />

Then you can locate each image with

$('img.ad')

[EDIT] Well, the server obviously knows which script belongs into which script tag because it inserts the script. So this is a no-brainer.

If the script wants to find out where it is in the DOM, add something which it can use to identify itself, say:

<script>var id= '329573485745';

Then you can walk all script tags and check which one contains the value of the variable id.

If you call an external script, then you can do the same but you must add the ID to the script tag as you emit the HTML:

<script id="329573485745" src="..." />

Then the external script can examine the DOM and lookup the element with this id. You will want to use an UUID for this, btw.

This way, a piece of JS can locate the script tag which added itself to the page.

Aaron Digulla
+1 for piecing out what this guy was trying to do!! LOL
used2could
Getting warmer. Locating script tags gives more clues. As for using attributes or special elements - no, I don't want to burden the poor template designer.
zaf
Well @zaf I suggest you look around the web. What Aaron describes is in fact what *everybody* actually does.
Pointy
@zaf: If you don't understand what I'm talking about, then I can't help because the problem is so simple, anyone with a bit of knowledge of JavaScript can do it in a few minutes. AKA, the problem is too simple to explain.
Aaron Digulla
@zaf: On top of that, you're paying the template designer to do what is his job. They do things like that ten times every day. If he can't handle that, he's not worth his money (true even if he works for free).
Aaron Digulla
@Pointy Can't be everybody because I don't :)
zaf
Well good luck with that.
Pointy
A: 

The term "static block of code" leaves a lot of room for interpretation.

Inline scripts (e.g., ones that rely on document.write and so must be parsed and executed during the HTML parsing phase) cannot tell where they are in the DOM at runtime. You have to tell them (as in one of the first answers you got).

I think you'll probably find that you need to change your approach.

A common way to keep code and markup separate (which is useful when providing tools to HTML designers who aren't coders) is to have them use a script tag like so:

<script defer async type='text/javascript' src='pagestuff.js'></script>

...which then triggers itself when the page is loaded (using window.onload if necessary, but there are several techniques for being triggered earlier than that, which you want because window.onload doesn't trigger until the images have all loaded).

That script then looks for markers in the markup and manipulates the page accordingly. For instance (this example uses Prototype, but you can do the same with raw JavaScript, jQuery, Closure, etc.):

document.observe("dom:loaded", initPage);
function initPage() {
    var verticals = $$('div.vertical');
    /* ...do something with the array of "vertical" divs in `verticals`,
       such as: */
    var index;
    for (index = 0; index < verticals.length; ++index) {
        vertical.update("I'm vertical #" + index);
    }
}

The designers can then have blocks on the page that are filled in by code which they flag up in a way that's normal for them (classes or attributes, etc.). The code figures out what it should do based on the classes/attributes of the blocks it finds when it runs.

T.J. Crowder
+1  A: 

Best thing would probably be to make an insert once function, and then have him insert only the function call where needed.

Like this:

timescalled=0
function buildad(){
    var toinsert="" //Code to generate the desired piece of HTML
    document.write(toinsert)
    timescalled+=1 //So you can tell how many times the function have been called
}

Now a script block calling the function can simply be inserted wherever a banner is needed

<script type="text/javascript">buildad()</script>
eBusiness
Is there a guarantee that script tags get executed one after the other? If not then your function won't help.
zaf
@zaf: Yes, there is, script tags are executed in strict document order unless they have the `defer` or `async` attribute. Each script tag holds up the HTML parsing and hands off to the JavaScript interpreter, and then when the JavaScript interpreter is done with the block the HTML parser continues. This is why `document.write` works, but it's not only true of blocks containing `document.write`.
T.J. Crowder
@eBusiness: +1 for getting close to what he actually wants to do. Off-topic: Although JavaScript has this notion of "semicolon insertion," it's *very bad idea* to actually use it. Always include the semicolons, not least because they make your intent clear (to other people, and to the interpreter) and they make it possible to use minifiers/compressors/packers, which don't work with code that relies on semicolon insertion.
T.J. Crowder
Of all the silly problems I have had, lack of semicolons in JavaScript has never been a cause. I don't see why it is such a big issue to some people, I suppose it has a lot to do with style. Fair point about compressors though.
eBusiness
+1  A: 

Thanks for the tips everyone but I'll be answering my own question.

I figured out several ways of accomplishing the task and I give you the one which works nicely and is easy to understand.

The following chunk of code relies on outputting dummy divs and jQuery.

<script>
    // Unique identifier for all dummy divs
    var rnd1="_0xDEFEC8ED_";
    // Unique identifier for this dummy div
    var rnd2=Math.floor(Math.random()*999999);
    // The dummy div
    var d="<div class='"+rnd1+" "+rnd2+"'></div>";
    // Script which :
    // Calculates index of THIS dummy div
    // Total dummy divs
    // Outputs to dummy div for debugging
    var f1="<script>$(document).ready(function(){";
    var f2="var i=$('."+rnd1+"').index($('."+rnd2+"'))+1;";
    var f3="var t=$('."+rnd1+"').length;";
    var f4="$('."+rnd2+"').html(i+' / '+t);";
    var f5="});<\/script>";
    document.write(d+f1+f2+f3+f4+f5);
</script>
zaf