Nick
Nick

Reputation: 675

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?

Upvotes: 0

Views: 584

Answers (2)

marqueemoon
marqueemoon

Reputation: 401

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

Upvotes: 1

jsbueno
jsbueno

Reputation: 110146

This is nothing common sense can't work for you - and Brython in this respect does just the same as Javascript: any DOM element you want to change needs to exist before you try to modify/bind it.

For decades the "usual" way to do that in Javascript has been place the bindings in a function and just call it at the bottom of the page, or on the body tag onload event, after everything else is loaded. "Modern" Javascript code "solves" this by using jQuery or other framework and its ready() method.

You have to do the same there - the timer might work, but it is risky. And, of course, elements that just exist after one or more of the other functions are triggered should be dealt with inside the respective functions:

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

Upvotes: 0

Related Questions