0

I made a little script that makes my JS initialization in Partial Pages a bit easier. It simply searches for all data-onload attributes, and executes the function defined there on-load. There is also some other functionality. So is the data-onload called automatically when that specific partial view is loaded through an AJAX call.

Anyway, the syntax looks like this:

<div class="some-partial-html-stuff">
    <button>[...]</button>
</div>

<script data-onload="partialInit">

    function partialInit()
    {
       // executes onload and on-ajax-load stuff for this Partial Page
        $('.some-partial-html-stuff button').doSomething();
     }

    function otherFunctions()
    {
        // [...]
    }

</script>

The only thing that I still would love to tackle is that right now I need to have a unique functionName for every partial page (otherwise the names will clash when they are both loaded). So I have manageProfileInit(), editImageInit() etc.

Now is the OCD-devil in me wondering if there is some way to clean this up even further (without too many negative consequences). I would love to have the situation where I can have a simple clean functon init() in any scriptblocks, and have the same funcionality described above.

Of course in the current situation all the functions will override each other. But does anyone know a nice trick or workaround how this could work?

To summarize, I want to make a script that makes sure this will work on every Partial Page, without any clashes.

<div class="some-partial-html-stuff">
    <button>[...]</button>
</div>

<script data-autoinit>

    function init()
    {
        // this method is automatically called if the 'data-autoinit' is defined
        // executes onload and on-ajax-load stuff for this Partial Page
        $('.some-partial-html-stuff button').doSomething();
    }

</script>
3
  • Err... why are you loading JavaScript dynamically like that? The building blocks in JS are functions, if you want to organize things in bigger scale take a look at the module pattern - or even easier in your case - module loaders. Commented Feb 20, 2014 at 22:06
  • I'm using Knockout.js, and these are relative small scripts that belong to the html in the same PartialPage. Because of maintenance, I like to keep the script and the corresponding html as close as possible together. Commented Feb 20, 2014 at 22:11
  • I must admit that I'm not really familiar with module loaders. But my understanding was that they were more for importing .js files, than for executing initialization stuff when the dom is loaded? Commented Feb 20, 2014 at 22:13

1 Answer 1

1

When I do stuff like this, I call them features. Tags look like this:

<div data-feature="featureName"></div>

Then we get all of the tags that have the data-feature tag and loop over them, creating an array of features the page is going to use:

var featureObjects = $('[data-feature]');
var features = [];
if ( !featureObjects.length ) return false;
for ( var i = 0, j=featureObjects.length; i<j; i++ ) {
    var feature = $(featureObjects[i]).data('features');
    if ($.inArray(feature, features) == -1){
        if (feature !== ""){
            features.push(feature);
        }
    }
};

Now you'll want to load the JS file asychronously and call it's init function once it's loaded:

for (var i=0, j=features.length; i<j; i++){
    var feature = features[i];
    $.ajax({
        url: "path/to/js/" + feature + ".js",
        dataType: "script",
        async: false,
        success: function () {
            App.features[feature].init();
        },
        error: function () {
            throw new Error("Could not load script " + script);
        }
    });
}

The actual modules look like this and attach themselves to App.features for later use:

App.features.featureName = (function(feature){

    // INIT FUNCTION
    feature.init = function(){

    };

    return feature;
}(App.features.featureName || {}));

Just remember to make sure App.features is an array before doing all of this, hopefully somewhere towards the top of your main.js file. I keep other functionality such as helpers and utilities in the app, so I usually kick it off with something like:

var App = {
    utilities: {},
    features: {},
    helpers: {},
    constants: {}
};

Now you can just tag DOM objects with a data-feature tag and functionality will be added automatically and as-needed, keeping a nice tie between specific JavaScript and specific DOM, but without the need of having to keep the JS inline next to the actual DOM. It also makes those "blurbs" re-usable should they need to be used elsewhere, which lowers maintenance overhead when working on your application.

Sign up to request clarification or add additional context in comments.

5 Comments

Not really exactly the answer I was looking for, but very interesting nonetheless! Thing is, that for the moment I actually do want to keep the JavaScript close to the html. The pieces are very tightly coupled one-offs, because they are often specific ViewModels of Knockout.js. So I want to prevent jumping around too often for now :)
And because these are very specific one-offs, I rather don't want to think about the naming of the thing. So I was wondering if there would be a way to say, always execute init() in the current scriptblock. So I don't have to name it page1Init, controlXInit, etc
The problem you are going to run into is scoping. Since all of the init functions are in the same scope, each assignment to init clears the previous assignment. You'd be better off setting each init as a self-calling function if possible, i.e. init = function(){//do stuff}() so that each init function calls itself and then clears itself from the scope immediately after running. Although this might not be possible depending on when you need the init to run.
true :) needs to be called after dom-load and some globals init, so wouldn't work in this case. I was hoping that their would be some strange javascript ninja-trick that I could use for this.
In your global init you could do window.inits = [], and then instead of declaring var init = function(){//dostuff} declare window.inits[window.inits.length] = function(){//dostuff} and then when the page is loaded loop over the window.inits array and call each one.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.