pythonhtmlchromiumplaywrightplaywright-python

TypeError: 'Locator' object is not callable using .first() in Playwright


I have a button on a webpage which looks like this:

<button rpl="" aria-controls="comment-children" aria-expanded="true" aria-label="Toggle Comment Thread" class="text-neutral-content-strong bg-neutral-background overflow-visible w-md h-md
button-small px-[var(--rem6)]
button-plain


icon
items-center justify-center
button inline-flex "> <!--?lit$747127195$--><!----><span class="flex items-center justify-center"> <!--?lit$747127195$--><span class="flex"><!--?lit$747127195$--><svg rpl="" fill="currentColor" height="16" icon-name="leave-outline" viewBox="0 0 20 20" width="16" xmlns="http://www.w3.org/2000/svg"> <!--?lit$747127195$--><!--?lit$747127195$--><path d="M14 10.625H6v-1.25h8v1.25ZM20 10a10 10 0 1 0-10 10 10.011 10.011 0 0 0 10-10Zm-1.25 0A8.75 8.75 0 1 1 10 1.25 8.76 8.76 0 0 1 18.75 10Z"></path><!--?--> </svg></span> <!--?lit$747127195$--> </span> <!--?lit$747127195$--><!--?--><!----><!----><!----> </button>

I want to press it with Playwright in my Python code. I wrote:

page.locator('button[aria-label="Toggle Comment Thread"]').first().click()

Sadly, it is giving me the error TypeError: 'Locator' object is not callable. When I don't use first(), it says

Error: Error: strict mode violation: locator("button[aria-label=\"Toggle Comment Thread\"]") resolved to 3 elements:

which is right, there are 3 of the buttons on the page. I just want to click the first one.


Solution

  • Try .first rather than .first():

    page.locator('button[aria-label="Toggle Comment Thread"]').first.click()
    

    Runnable example:

    from playwright.sync_api import sync_playwright # 1.40.0
    
    html = '<button aria-label="Toggle Comment Thread"></button>'
    
    with sync_playwright() as p:
        browser = p.chromium.launch()
        page = browser.new_page()
        page.set_content(html)
        page.locator('button[aria-label="Toggle Comment Thread"]').first.click()
        browser.close()
    

    That said, get_by_role is preferred:

    page.get_by_role("button", name="Toggle Comment Thread").first.click()
    

    And try to avoid .first; find a stricter way to uniquely identify the element (possibly using one of its other many properties) rather than relying on element ordering on the page, which is liable to change unexpectedly.

    This isn't the only occurrence of this API difference. page.url is also a property, not a function.