5

Mid development I decided to switch to server-side rendering for a better control amongst other benefits. My web application is completely AJAX based, no url redirecting, so the idea here is a website that builds itself up

I just couldn't figure out the proper way to send javascript events/functions along with the html string, or should all the necessary javascript always be preloaded in the static files?

Let's say client clicks a pre-rendered button 'open table'

The server will make a query, build the html table and send it back, but this table also needs javascript triggers and functions to work properly, how are these sent, received and executed?

There are a couple of articles that mention to not use eval() in Javascript, is there any way around this? I don't want to have to preload unnecessary events for elements that don't yet exist

The server is Python and the Client is Javascript/JQuery

Theoretical example :

Client Base Javascript :

$("body").on("click", "#open_table", function() {

    $.getJSON('/get_table', function(response){

        $("#table_div").append(response.html);
        eval(response.javascript()); //??
    }
});

Python Server(views.py) :

def get_table(request):
    data = {}
    #String containing rendered html
    data['html'] = get_render_table()
    #String containing Javascript code?
    data['javascript'] = TABLE_EVENTS_JAVASCRIPT
    return HttpResponse(json.dumps(data),content_type='json')

Worth noting my question comes from an experimental/learning perspective

Mojimi
  • 2,561
  • 9
  • 52
  • 116
  • Why does the response need different triggers from the original page? Usually you would set triggers on elements by class, and the new HTML would contain elements with those classes to pick up the triggers automatically. – Daniel Roseman Dec 12 '16 at 18:45
  • @DanielRoseman Mostly experimental, in my thoughts, why should I load an event for something that doesn't exist(yet) and might not exist? This gets problematic when your website is just one page – Mojimi Dec 12 '16 at 18:49
  • 1
    As I understand eval() might not be good idea because: 1) it's relatively slow, 2) it's potentially dangerous. But if that JS coming from server would be just simple triggers and it always comes from secure source, it doesn't seem to be wrong. – Michał Werner Dec 13 '16 at 01:19
  • 1
    Could you return your JavaScript to the client and have a small amount of JavaScript already loaded front-end that would create a `script` tag in the DOM? – Connor Gurney Dec 13 '16 at 02:09

4 Answers4

7

Update:

You can use jQuery.getScript() to lazy load JS. I think this solution is as close as you can get to run JS without using eval().

See this example:

jQuery.getScript("/path/to/script.js", function(data, textStatus, jqxhr) {
    /* Code has been loaded and executed. */
    console.log( data ); // Data returned
    console.log( textStatus ); // Success
    console.log( jqxhr.status ); // 200
    console.log( "Load was performed." );
});

and "/path/to/script.js" could be a string returned from $.getJOSN response.

Also, the documentation for getScrippt() has examples on how to handle errors and cache files.

Old Answer:

Using .on() attaches events to current and future DOM elements. You can either attache events prior to DOM insertion or attache event after DOM insertion.

So in your example you can do something like:

$("body").on("click", "#open_table", function() {
$.getJSON('/get_table', function(response){
 var code = $(response.html);
   code.find(".elementToFind").on("click", function (){
 // Code to be executed on click event
});
    $("#table_div").append(code);
}
});

I did not test the code but I think it should work.

Kalimah
  • 11,217
  • 11
  • 43
  • 80
1

Assuming you can't just set up an event-binding function and then call it from the main script (the JavaScript you need can't be guessed ahead of time, for example) then one really easy way is just to append the JavaScript to the bottom of the returned HTML content within script tags. When it's appended along with the HTML, the script should simply execute, with no eval() required.

I can't swear that this would work in old browsers, but it's a trick I've used a couple of times, and I've had no problems with it in Firefox, Chrome, or any of the later IE versions.

Rob Wilkins
  • 1,650
  • 2
  • 16
  • 20
1

I think I see what you're asking here, from my understanding you want to send the new "page" asynchorously, and render the new javascript and html. It looks like you already got your request/response down, so i'm not gonna go and talk about sending JSON objects, and the whole "how-to" of sending html and javascript because it looks like you got that part. To do what you want and to dynamically add your javascript in, this stackoverflow question looks like it has what you need Is there a way to create a function from a string with javascript?

So pertaining to your example, here is how it would look when you recieve the JSON string from your python script:

$("body").on("click", "#open_table", function() {

    $.getJSON('/get_table', function(response){

        $("#table_div").append(response.html);

        /* Create function from string */
        var newFunction = Function(response.javascript['param_1'], response.javascript['param_2'], response.javascript['function']);

        /* Execute our new function to test it */
        newFunction();

    }
});

*Your actual function contents would be the string: response.javascript['function']
*Your parameter names if any would be in separate strings ex: response.javascript['param_1']

That is almost a direct copy of the "String to function" code that you can see in the linked question, just replaced it with your relevant code. This code is also assuming that your object is sent with the response.javascript object containing an array with your actual function content and parameter names. I'm sure you could change the actual name of the var too, or maybe put it in an associative array or something that you can keep track of and rename. All just suggestions, but hopefully this works for you, and helps you with your problem.

Community
  • 1
  • 1
Iqbal Khan
  • 363
  • 2
  • 6
  • 22
1

I am also doing similar work in my project where I had to load partial html using ajax calls and then this partial HTML has elements which requires events to be attached. So my solution is to create a common method to make ajax calls and keep a js method name to be executed post ajax call in html response itself. For example my server returns below html

<input type="hidden" data-act="onPartialLoad" value="createTableEvents" />
<div>.........rest of html response.....<div>

Now in common method, look for input[type='hidden'][data-act='onPartialLoad'] and for each run the method name provided in value attribute (value="createTableEvents")

Dont Use Eval() method as it is not recommended due to security issues. Check here.

you can run js method using window["method name"]...so here is a part of code that I use.

$.ajax(options).done(function (data) {
                
                var $target = $("#table_div");      
                $target.fadeOut(function () {
                    $target.html(data);
                    $target.fadeIn(function () {
                        try {

                            $('input[data-act="onPartialLoad"]', $target).each(function () {
                                try {
                                  //you can pass parameters in json format from server to be passed into your js method
                                    var params = $(this).attr('params');
                                    if (params == undefined) {
                                        window[$(this).val()]();
                                    }
                                    else {
                                        window[$(this).val()]($.parseJSON(htmlutil.htmlDecode(params)));
                                    }
                                } catch (e) {
                                    if (console && console.log) {
                                        console.log(e.stack);
                                        console.log($(this).val());
                                    }
                                }
                            });
                        }
                        catch (e) {
                            console.log(e.stack);
                        }
                    });
                });
            });

use jQuery.getScript() (as suggested by Kalimah Apps) to load the required js files first.

Community
  • 1
  • 1
pgcan
  • 1,199
  • 14
  • 24