The same everyday operations - click an element, fill in a field, read a value - look different at each of GPAL's five layers. This tutorial walks Click and FillIn side by side across all five, then digs into Read: GPAL's GetGrid convenience, the UseAttribute selector setting that controls what GetGrid returns, and how reading a single attribute looks at each lower layer.
At the GPAL layer, WithSelector plus LeftClick is one step - GPAL finds the element and clicks it. At the other layers, the elementId argument can simply be the CSS selector itself (an element's elem.Css is just the selector that found it), so RESTClient, MagicHelper, and PuppeteerClient can target an element with the same selector string you'd pass to WithSelector - no separate QuerySelector/ElementHandle step needed for a single, specific element. PuppeteerCommunicator's LeftClickByCss takes a CSS selector directly too, collapsing both steps into one the way GPAL does. QuerySelector + ElementHandle only becomes necessary when a generic selector matches many elements and you need to act on one specific match from that set.
// GPAL
browser.WithSelector("#submit-btn").LeftClick();
// RESTClient
GPAL.OttoMagicClient.LeftClick("#submit-btn").Execute();
// MagicHelper
GPAL.MagicHelper.LeftClick("#submit-btn");
// PuppeteerClient
GPAL.PuppeteerClient.LeftClick("#submit-btn").Execute();
// PuppeteerCommunicator
await communicator.LeftClickByCss("#submit-btn");
GPAL's FillInFrom takes the text directly. At RESTClient, MagicHelper, and PuppeteerClient, FillInOverwrite (or FillInAppend / FillInInsert for the non-destructive variants) accepts the CSS selector as its element identifier, just like LeftClick - elem.Css is the same string you'd pass to WithSelector. PuppeteerCommunicator sits one level lower and works with a resolved object handle rather than a selector, so it needs QuerySelector first to obtain elem.ElementHandle.
// GPAL
browser.WithSelector("#email").FillInFrom("user@example.com");
// RESTClient
GPAL.OttoMagicClient.FillInOverwrite("#email").WithText("user@example.com").Execute();
// MagicHelper
GPAL.MagicHelper.FillInOverwrite("#email", "user@example.com");
// PuppeteerClient
GPAL.PuppeteerClient.FillInOverwrite("#email").WithText("user@example.com").Execute();
// PuppeteerCommunicator
var elem = await communicator.QuerySelector("#email");
await communicator.FillInOverwrite(elem.ElementHandle, "user@example.com");
Reading is where GPAL does the most for you, so it's the wrong place to start a layer-by-layer comparison - there isn't a one-line GetGrid equivalent at the lower layers. Instead, GetGrid is itself the convenience: for each matched element, GPAL inspects the GPALElement it already built while finding that element and picks a sensible default value for the grid - href for an <a>, src for an <img>, and Text for everything else. You never call GetAttribute yourself for the common cases.
browser
.WithSelector(linkColumn)
.WithSelector(imageColumn)
.WithSelector(descriptionColumn)
.WithAllThatMatch(10)
.GetGrid(out var grid);
// grid column 0 -> each <a>'s href
// grid column 1 -> each <img>'s src
// grid column 2 -> each element's Text
These per-tag defaults come from the same GPALElement that LeftClick and FillInOverwrite use to find the element - GetGrid doesn't make any extra round trips per element to read these values.
Sometimes the tag-based default isn't the value you want - you need an <img>'s alt text instead of its src, or an <input>'s value instead of its (empty) Text. Selector.UseAttribute(attributeName) overrides the default for that selector's column: instead of href/src/Text, GetGrid reads the named attribute from each matched element for that column.
Selector imageColumn = "//img[contains(@class, 's-card__image')]";
imageColumn.WithSelectorName("ImageAlt").UseAttribute("alt");
Selector priceInput = "#price-input";
priceInput.WithSelectorName("PriceValue").UseAttribute("value");
browser
.WithSelector(imageColumn)
.WithSelector(priceInput)
.WithAllThatMatch(10)
.GetGrid(out var grid);
// grid column 0 -> each <img>'s alt text (not its src)
// grid column 1 -> each <input>'s value attribute
UseAttribute is set on the Selector itself, so different columns in the same GetGrid call can each read a different attribute - or fall back to the tag-based default by simply not calling UseAttribute on that selector.
Below GPAL, there is no grid - you read one attribute at a time with GetAttribute. RESTClient, MagicHelper, and PuppeteerClient accept the CSS selector as the element identifier directly (elem.Css, the same string used with WithSelector), then chain WithAttribute before Execute<string> (RESTClient/PuppeteerClient) or pass the attribute name directly (MagicHelper). PuppeteerCommunicator's GetAttribute works at the CDP level and takes the element's backend node ID as an int, so it needs QuerySelector first to obtain elem.ElementBackendNodeId.
// RESTClient
string alt = GPAL.OttoMagicClient.GetAttribute("img.logo").WithAttribute("alt").Execute<string>();
// MagicHelper
string alt = GPAL.MagicHelper.GetAttribute("img.logo", "alt");
// PuppeteerClient
string alt = GPAL.PuppeteerClient.GetAttribute("img.logo").WithAttribute("alt").Execute<string>();
// PuppeteerCommunicator
var elem = await communicator.QuerySelector("img.logo");
var alt = await communicator.GetAttribute(elem.ElementBackendNodeId, "alt");
GetAttribute means the same thing at every layer below GPAL - only how you obtain the element handle and how the result comes back changes. This is the pattern Under the Hood describes: pick the altitude that matches how much you want GPAL to manage for you. At the GPAL layer, GetGrid plus UseAttribute covers the vast majority of cases without ever calling GetAttribute directly.
Showing off some plain text in these paragraphs eligendi laboriosam illo nostrum corporis at libero vel voluptas? Expedita, facere dolores voluptatem ad ab rem assumenda soluta!
Lorem ipsum dolor sit amet consectetur adipisicing elit. Obcaecati, iste distinctio veritatis eligendi laboriosam illo nostrum corporis at libero vel voluptas? Expedita, facere dolores voluptatem ad ab rem assumenda soluta!
Lorem ipsum dolor sit amet consectetur adipisicing elit. Obcaecati, iste distinctio veritatis eligendi laboriosam illo nostrum corporis at libero vel voluptas? Expedita, facere dolores voluptatem ad ab rem assumenda soluta!
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quo veniam mollitia excepturi animi eum illum non libero sapiente provident assumenda, delectus voluptatum nobis sed dolorem adipisci laudantium incidunt. Error, ratione?
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quo veniam mollitia excepturi animi eum illum non libero sapiente provident assumenda, delectus voluptatum nobis sed dolorem adipisci laudantium incidunt. Error, ratione?
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quo veniam mollitia excepturi animi eum illum non libero sapiente provident assumenda, delectus voluptatum nobis sed dolorem adipisci laudantium incidunt. Error, ratione?
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quo veniam mollitia excepturi animi eum illum non libero sapiente provident assumenda, delectus voluptatum nobis sed dolorem adipisci laudantium incidunt. Error, ratione?
Here you can find different accents and emphasis sit amet consectetur adipisicing elit. Obcaecati, iste distinctio veritatis eligendi laboriosam illo nostrum corporis at libero vel voluptas? Expedita, facere dolores voluptatem ad ab rem assumenda soluta!
This is a link and how it could look like bestlinkinthebeautifulworld. Obcaecati, iste distinctio veritatis eligendi laboriosam illo nostrum corporis at libero vel voluptas? Expedita, facere dolores voluptatem ad ab rem assumenda soluta!
Here's just some classic bold text adipisicing elit. Obcaecati, iste distinctio veritatis eligendi laboriosam notBoldSecondbestlinkinthebeautifulworld illo nostrum corporis at libero vel voluptas? Expedita, facere dolores voluptatem ad ab rem assumenda soluta!
Obcaecati, iste distinctio veritatis eligendi laboriosam adipisicing elit illo nostrum corporis at adipisicing elit libero vel voluptas? Expedita, adipisicing facere dolores voluptatem ad ab rem assumenda soluta!
Other cuple of colors in case we want to emphasize several ways adipisicing elit. Obcaecati, iste distinctio veritatis eligendi laboriosam adipisicing elit illo nostrum corporis at voluptatem libero vel voluptas? Expedita, facere dolores voluptatem ad ab rem assumenda soluta!
Lorem ipsum dolor sit amet consectetur adipisicing elit. Obcaecati, iste distinctio veritatis eligendi laboriosam illo nostrum corporis at libero vel voluptas? Expedita, facere dolores voluptatem ad ab rem assumenda soluta! Lorem ipsum dolor, sit amet consectetur adipisicing elit. Quod veniam, quam ad expedita laborum sed at voluptates culpa ipsam ut vel. Ullam temporibus a mollitia quod aliquam ratione exercitationem nesciunt.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Obcaecati, iste distinctio veritatis eligendi laboriosam illo nostrum corporis at libero vel voluptas? Expedita, facere dolores voluptatem ad ab rem assumenda soluta! Lorem ipsum dolor, sit amet consectetur adipisicing elit. Quod veniam, quam ad expedita laborum sed at voluptates culpa ipsam ut vel. Ullam temporibus a mollitia quod aliquam ratione exercitationem nesciunt.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Obcaecati, iste distinctio veritatis eligendi laboriosam illo nostrum corporis at libero vel voluptas? Expedita, facere dolores voluptatem ad ab rem assumenda soluta!
Lorem ipsum dolor sit amet consectetur adipisicing elit. Repudiandae quas consequuntur illo numquam assumenda autem exercitationem distinctio perspiciatis in natus. Eius dicta similique ipsam ipsa minima, nemo quae enim tempore.
GPAL
.CallIfNotFound(GenericCallIfNotFound)
.WithPublishToConsole();
//System.Drawing.Rectangle windowSize = new System.Drawing.Rectangle(10, 10, 1500, 1024);
// NOTE: we have to set browser = before we execute any steps
// this is due to the 'GenericCallIfNotFound' which might throw an exception, and BankScraper will not have the browser set when it calls scraper.Close()
// until the complete fluent line gets executed (meaning every step, meaning browser is not set until everything else succeeds)
browser = GPAL.Browser
.WithBrowserType(Enums.BrowserType.Chrome)
.WithProfileDataDirectory(ChromeProfileLocation)
.WithUseAutomationEngine(AutomationEngine.Selenium)
.WithWindowSize(new System.Drawing.Rectangle(0,0,1920,1080))
.ToGPALObject();