question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Add focus support to BrowserRenderer

See original GitHub issue

Is your feature request related to a problem? Please describe.

To set focus on an element from Blazor, currently requires and ElementReference (or an id) and a JSInterop call in OnAfterRenderAsync. It would be nice to be able to do this declaratively by using the “autofocus” attribute.

This is a capability of HTML, but does not work for SPA applications where the elements are inserted into an existing DOM.

The addition of an ability to have autofocus on newly created elements would make the SPA developer experience much simpler and provide a better result for the end user of the application.

Describe the solution you’d like

The Blazor application renders an element with the autofocus attribute and that triggers the BrowserRenderer to call the focus() method on the newly create element.

<button @onclick=@(MyClickHandler) autofocus>Click Me</button>

This should only cover initial element creation to maintain consistency with normal html autofocus.

Additional context

The BrowserRenderer used in Blazor can be modified in a manner similar to this (proof of concept testing confirms this at a superficial level) to provide autofocus on element creation.

private insertElement(batch: RenderBatch, componentId: number, parent: LogicalElement, childIndex: number, frames: ArrayValues<RenderTreeFrame>, frame: RenderTreeFrame, frameIndex: number) {
    const frameReader = batch.frameReader;
    const tagName = frameReader.elementName(frame)!;
    const newDomElementRaw = tagName === 'svg' || isSvgElement(parent) ?
      document.createElementNS('http://www.w3.org/2000/svg', tagName) :
      document.createElement(tagName);
    const newElement = toLogicalElement(newDomElementRaw);
    insertLogicalChild(newDomElementRaw, parent, childIndex);

+    // Handle autofocus
+    let wantsFocus: boolean = false;
    // Apply attributes
    const descendantsEndIndexExcl = frameIndex + frameReader.subtreeLength(frame);
    for (let descendantIndex = frameIndex + 1; descendantIndex < descendantsEndIndexExcl; descendantIndex++) {
      const descendantFrame = batch.referenceFramesEntry(frames, descendantIndex);
      if (frameReader.frameType(descendantFrame) === FrameType.attribute) {
        this.applyAttribute(batch, componentId, newDomElementRaw, descendantFrame);
+        // Handle autofocus
+        let attrName = batch.frameReader.attributeName(descendantFrame);
+        wantsFocus = ( attrName === 'autofocus' );
      } else {
        // As soon as we see a non-attribute child, all the subsequent child frames are
        // not attributes, so bail out and insert the remnants recursively
        this.insertFrameRange(batch, componentId, newElement, 0, frames, descendantIndex, descendantsEndIndexExcl);
        break;
      }
    }

+    if (wantsFocus) { // Handle autofocus
+      newDomElementRaw.focus();
+    }
    
    // We handle setting 'value' on a <select> in two different ways:
    // [1] When inserting a corresponding <option>, in case you're dynamically adding options
    // [2] After we finish inserting the <select>, in case the descendant options are being
    //     added as an opaque markup block rather than individually
    // Right here we implement [2]
    if (newDomElementRaw instanceof HTMLSelectElement && selectValuePropname in newDomElementRaw) {
      const selectValue = newDomElementRaw[selectValuePropname];
      newDomElementRaw.value = selectValue;
      delete newDomElementRaw[selectValuePropname];
    }
  }

Link to gist with full source : https://gist.github.com/SQL-MisterMagoo/949f2aff8aa0006ab6843bcedd14dd62/revisions

EDIT: 30/11/2019 Section below should be considered removed from this request as it was flawed

~~### Additional context At this point in the code, it would be simple to add another case statement to handle autofocus

https://github.com/aspnet/AspNetCore/blob/32a2cc594363672ccfe7644a649f77a8bfc9c4a8/src/Components/Web.JS/src/Rendering/BrowserRenderer.ts#L311

  private tryApplySpecialProperty(batch: RenderBatch, element: Element, attributeName: string, attributeFrame: RenderTreeFrame | null) {
    switch (attributeName) {
      case 'value':
        return this.tryApplyValueProperty(batch, element, attributeFrame);
      case 'checked':
        return this.tryApplyCheckedProperty(batch, element, attributeFrame);

       /* ** Suggested addition ** */
      case 'autofocus': {
          element.focus();
          return true;
        }

      default: {
        if (attributeName.startsWith(internalAttributeNamePrefix)) {
          this.applyInternalAttribute(batch, element, attributeName.substring(internalAttributeNamePrefix.length), attributeFrame);
          return true;
        }
        return false;
      }
    }
  }
```~~

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Reactions:21
  • Comments:18 (15 by maintainers)

github_iconTop GitHub Comments

4reactions
javiercncommented, Nov 29, 2019

We’ll consider this feature during the next release planning period and update the status of this issue accordingly.

3reactions
SQL-MisterMagoocommented, Nov 30, 2019

@ all I have modified the original message (I left in the original as people have “upvoted”)

The reason was that I finally got around to testing and realised my original suggestion was unworkable.

This alternative is meant as a jumping off point - for cleverer people than me to discuss, but minimal testing shows it to work very nicely.

@egil I think a separate issue for your suggestion would be a good thing and I would be happy to support it, but we are in danger of discussing two slightly different things in one issue, I think. If they are two separate issues, one can be closed without affecting the other.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Add an API to focus on elements · Issue #22892
To solve this, we'd like to introduce FocusAsync() as an extension method to ElementRef types. Here's the usage we think would work the...
Read more >
org.hippoecm.frontend.service.IRenderService.focus java ...
Set focus on the specified child. Implementations should make the child visible when they themselves are visible, or become visible later. Popular methods...
Read more >
Set Chrome policies for users or browsers
Enrolled browsers to enforce policies when users open Chrome browser on managed Microsoft Windows, Apple Mac, or Linux computers. Signing in is not...
Read more >
Features overview
GrapheneOS is focused on substance rather than branding and marketing. ... GrapheneOS has a compatibility layer providing the option to install and use...
Read more >
Hyperfocus: The ADHD Phenomenon of Intense Fixation
Hyperfocus refers to an intense fixation on an interest or activity for an extended period of time. Children and adults with ADHD often ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found