Search code examples
javascriptpythonhtmlbrython

Brython bind a click event to an id that is not yet in the page


So I have the following dilemma:

I am using Brython and everything is working ok. I have a small piece of code that executes ajax requests for me and I added that in the header to bind everything on the current elements in the page.

    from browser import document, ajax

# URL Query String
qs = ''
# URL to work on
url = ''


def post_data(url, qs):
    req = ajax.ajax()
    # Bind the complete State to the on_post_complete function
    req.bind('complete', on_post_complete)
    # send a POST request to the url
    req.open('POST', url, True)
    req.set_header('content-type', 'application/x-www-form-urlencoded')
    # send data as a dictionary
    req.send(qs)


def get_data(url, qs):
    req = ajax.ajax()
    req.bind('complete', on_get_complete)
    # Bind the complete State to the on_get_complete function
    req.open('GET', url+'?'+qs, True)
    req.set_header('content-type', 'application/x-www-form-urlencoded')
    req.send()


def on_post_complete(req):
    if req.status == 200 or req.status == 0:
        #  Take our response and inject it into the html div with id='main'
        document["main_area"].html = req.text
    else:
        document["main_area"].html = "error " + req.text


def on_get_complete(req):
    if req.status == 200 or req.status == 0:
        #  Take our response and inject it into the html div with id='main'
        document["main_area"].html = req.text
    else:
        document["main_area"].html = "error " + req.text


def account_click(ev):
    get_data("/account", qs)


def contact_link_click(ev):
    get_data("/contact", qs)


def logo_link_click(ev):
    get_data("/main_page", qs)


def products_link_click(ev):
    get_data("/products_page", qs)


def register_link_click(ev):
    get_data("/register", qs)

document['login_link'].bind('click', account_click)
document['contact_link'].bind('click', contact_link_click)
document['logo_link'].bind('click', logo_link_click)
document['register_link'].bind('click', register_link_click)

document['running_link'].bind('click', products_link_click)
document['fitness_link'].bind('click', products_link_click)
document['tennis_link'].bind('click', products_link_click)
document['football_link'].bind('click', products_link_click)
document['golf_link'].bind('click', products_link_click)

Ok now my bigger problem is the fact that register_link is not in the page from the beginning. To be more exact register_link will only be loaded into the DOM after the login_link link is clicked after which the register link does nothing because the event was unable to be bound on it from the get go.

Now I know that I could easily bypass this just by importing this again in that page but I would want to avoid redundant imports and i'm not really sure exactly how to go about doing this.

EDIT: Or is there a way in brython to wait for the DOM to be loaded completely?


Solution

  • As you noticed, writing account_click like this :

    def account_click(ev):
        get_data("/account", qs)
        document['register_link'].active = True
        document['register_link'].bind('click', register_link_click)
    

    doesn't work, because the program doesn't wait for get_data to complete before executing the next 2 lines.

    A solution is to write a specific version of get_data and on_get_complete for this case (I have supposed that the "register_link" button is in the page, but initially disabled):

    def complete_register(req):
        """Called when the Ajax request after "login_link" is complete."""
        if req.status == 200 or req.status == 0:
            #  Take our response and inject it into the html div with id='main'
            document["main_area"].html = req.text
            # enable "register link" button and add binding
            document['register_link'].disabled = False
            document['register_link'].bind('click', register_link_click)
        else:
            document["main_area"].html = "error " + req.text
    
    def get_data_and_register(url, qs):
        req = ajax.ajax()
        req.bind('complete', complete_register)
        req.open('GET', url+'?'+qs, True)
        req.set_header('content-type', 'application/x-www-form-urlencoded')
        req.send()
    
    def account_click(ev):
        get_data_and_register("/account", qs)
    

    Another option would be to keep the generic functions get_data and on_get_complete, and add an optional parameter callback:

    def get_data(url, qs, callback=None):
        req = ajax.ajax()
        req.bind('complete', lambda req:on_get_complete(req, callback))
        # Bind the complete State to the on_get_complete function
        req.open('GET', url+'?'+qs, True)
        req.set_header('content-type', 'application/x-www-form-urlencoded')
        req.send()
    
    def on_get_complete(req, callback=None):
        if req.status == 200 or req.status == 0:
            #  Take our response and inject it into the html div with id='main'
            document["main_area"].html = req.text
            if callback is not None:
                callback(req)
        else:
            document["main_area"].html = "error " + req.text