Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ArrayHandle null trying to enumerate <li/> menu items #54

Open
mwpowellhtx opened this issue Jul 8, 2024 · 7 comments
Open

ArrayHandle null trying to enumerate <li/> menu items #54

mwpowellhtx opened this issue Jul 8, 2024 · 7 comments

Comments

@mwpowellhtx
Copy link

To frame the opportunity at hand, I tried the same thing in the Chrome dev tools in browser, and I get the range back that I am expecting.

// AH is null querying for the <li/> from the parent menu.
public async Task<T[]> QuerySelectorAllAsync<T>(string selector)
    where T : Element
{
    var arrayHandle = await EvaluateFunctionHandleInternalAsync<DomHandle>(
        "(element, selector) => element.querySelectorAll(selector)",
        selector).ConfigureAwait(false);

    // AH is null:
    var properties = await arrayHandle.GetArray<T>().ConfigureAwait(false);
    //                     ^^^^^^^^^^^
    await arrayHandle.DisposeAsync().ConfigureAwait(false);

    return properties.ToArray();
}

In my calling code:

// Selector "#menu", does return a valid element construct, AFAIK from debug inspection:
var ulMenu = await context.QuerySelectorAsync<HtmlUnorderedListElement>(S.Menu);

// Selector "li", null ref exception thrown from within, expecting HtmlListItemElement[].
var liCandidates = await ulMenu.QuerySelectorAllAsync<HtmlListItemElement>(S.Li);

// Will turn around and LINQ filter based on some other criteria, but not getting anywhere near that far.
var liServerItems = liCandidates.Where(ListItemWithImage).ToArray();
//                                     ^^^^^^^^^^^^^^^^^

I'm doing that correctly, yes? I want all the <li/> elements child to the ulMenu.
Everything else being equal, I promise you it "should be" returning valid elements, from the Chrome console:

document.querySelector("#menu").querySelectorAll("li")
// #menu: <ul class="..." id="menu" />
// li: NodeList(20) [li.mm-active, li, li, li, li, li, li, li, li, li, li, li, li, li, li, li, li, li, li, li, li]

There is obviously more within each of these elements, abbreviated for brevity.

@mwpowellhtx
Copy link
Author

Another data point for you, for giggles I tried the following.

var liContext = await context.QuerySelectorAllAsync<HtmlListItemElement>(S.Li);

And it "works". Sort it, but it will not work for us, I think, because these are "all" of the <li/>, not just the ones subordinate to the ulMenu that we want.

Maybe there is a better way to get those children, I do not know.

@mwpowellhtx
Copy link
Author

Note that the <li/> children are not all flat; it would be considered best for a depth first recursive search. I'm assuming that's how the console does it, but I could be mistaken.

@mwpowellhtx
Copy link
Author

After poking around a bit and exploring the query and child paths available, this one will work, and I rather prefer it actually. Seems to be focused on the direct child elements of the specified type, which is perfect, just what we need. Not that the other one ought not to work, in principle, but this one does as well, FWIW.

var ulMenu = await context.QuerySelectorAsync<HtmlUnorderedListElement>(S.Menu);
var liServerItems = await (await ulMenu.GetChildrenAsync<HtmlListItemElement>()).ToArrayAsync();
// ...and on from there, drilled through a couple other use cases, including another nested <li/>, also successfully.

The only thing I dislike about it, the lack of selector comprehension, in the event we need to provide more discernment there, but for what we are doing, I think it will be sufficient.

@amaitland
Copy link
Member

I'm doing that correctly, yes?

Your code looks fine, likely it's a bug. querySelectorAll returns a NodeList and there's currently no mapping for that yet.

The other QuerySelectorAllAsync methods do something slightly different. Something like the following should work.

var arrayHandle = await Handle.EvaluateFunctionHandleAsync(
    "(element, selector) => element.querySelectorAll(selector)",
    selector).ConfigureAwait(false);

var properties = await arrayHandle.GetPropertiesAsync().ConfigureAwait(false);
await arrayHandle.DisposeAsync().ConfigureAwait(false);

return properties.Select(kvp => kvp.Value.ToDomHandle<T>()).ToArray();

And it "works". Sort it, but it will not work for us, I think, because these are "all" of the <li/>, not just the ones subordinate to the ulMenu that we want.

Maybe there is a better way to get those children, I do not know.

You know querySelectorAll supports css selectors? You should be able to make a single querySelectorAll call

@mwpowellhtx
Copy link
Author

Awesome; duly noted, for future reference. Thank you... 🍻

@amaitland
Copy link
Member

We should fix the current implementation.

@amaitland amaitland reopened this Jul 12, 2024
@mwpowellhtx
Copy link
Author

Cool. That'd be great, overall in general.

Although, I do have a workaround, which I think is a bit better suited to the target DOM in question.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants