How can I set up the default keyboard control for Google's Model Viewer?

Trying to enhance accessibility, I am working on enabling the camera orbit of my model viewer using arrow keys immediately after dismissing the poster. The challenge is that currently, I can only control the camera with arrow keys after interacting with the model-viewer a second time (e.g., clicking to dismiss the poster and then needing to click on the 3D model at least once). This setup hinders accessibility.

Is there a way to make the model viewer accept keyboard input without the prerequisite of clicking or touching the 3D model first? I have attempted the following:

  1. Using the focus(); function on my model-viewer element, as well as on my .userInput div within it, and the canvas element inside that
  2. Setting the tab indexes of each element to 1, but it did not work
  3. Applying delegateFocus on the elements
  4. Utilizing the native enableInteraction(); function of model-viewer
  5. Adding an event listener when the poster is dismissed, using
    this.addEventListener('keydown', this[$modelViewer].value?.onKeyDown);

I confirmed that these elements are manipulated/appended correctly, suggesting that focusing and setting tab indexes do not directly enable model-viewer to receive keyboard input initially.

What steps should I take to compel model-viewer to accept keyboard input right after dismissing the poster and loading the 3D model?

Answer №1

If you need to call .focus() on your .userInput div, keep in mind that the <model-viewer> element generates a Shadow DOM. The purpose of this Shadow DOM is to encapsulate its child components, making it a bit tricky to access elements through classes like in a regular DOM.

To achieve this, you must first access the shadowRoot and then locate the correct <div> inside it. For instance, I successfully implemented this method on , where clicking anywhere on the document focuses the .userInput div. Here's the selection script that can be adjusted based on your requirements:

window.addEventListener("click", (evt) => {
    console.log("Focusing...");
    document.getElementsByTagName("model-viewer")[0].shadowRoot.children[1].getElementsByClassName("userInput")[0].focus();
});

Considering you have complete control over your <model-viewer>, this focusing technique may also be applicable during the load event.

Update:

If you aim to grant keyboard focus upon dismissing the poster with a click, utilizing the "click" event might be more suitable than waiting for the load event.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

An array consisting of functions that specifically retrieve keys from an object

I have a user object with properties like id and name. interface User { id: string; name: string; } There is also an UpdateUserBody type that allows updating only the 'name' property of the user in the backend. type UpdateUserBody = Pick< ...

Implementing ordering and limiting of elements in NestJS TypeORM relations

I am new to typeorm and relationships. I am currently facing some challenges and feeling a bit confused. I have a question regarding sorting relations in typeorm by id or createdDate without explicitly mapping them and limiting the number of elements. Here ...

Tips for Sending <Div> Data to a Servlet

I attempted to pass the content of an entire div in a form action URL using JavaScript. However, when I try to retrieve this request parameter as a String on the servlet side, it is returning [object Object]. Below is my code for the form and JavaScript: ...

Experiment with re-rendering advertisements using Vue.js

Hey there, I'm new around here so please be patient with me if I make any mistakes :D So, onto my question! I have a website built with vuejs and I want to incorporate some ads into it. To do this, I have created some components: <template> ...

Activate the stripe button after successful bootstrap validation

My goal was to implement BootstrapValidator for validation on a couple of fields and enable the Stripe button only when both fields are valid. Currently, the button is enabled once any of the fields pass validation. The challenge lies in ensuring that the ...

Storing the outcome of a connection in a variable using Node.js

I am facing an issue with saving a function return in a const so that I can utilize the information outside of the function scope. Below is a snippet of code to better explain my problem: const express = require('express') const app = express() ...

Components in array not displaying in React

I've been struggling to generate a table from an array in React. Typically, I retrieve data from a database, but for testing purposes, I manually created the array to ensure the data is correct. Despite following examples by enclosing my map code with ...

Tips for adding and deleting elements after cloning in jQuery

I'm trying to achieve a layout similar to the one shown in this picture. It should display a delete button for each item and an add button to add more items. Check out the example here. I have managed to display the buttons individually, but I'm ...

Merge information from two sources utilizing concatMap

I am encountering an issue where I need to extract a specific value from the data obtained from my first service in order to pass it on to the second service. Angular seems to have trouble with 'firstserviceResultsJson.indSsn'. How can I successf ...

What is the best way to create a fixed footer in Next.js using React?

I'm looking to create a fixed footer that will stay at the bottom of the page if there isn't enough content to fill it. I've been researching ways to achieve this using CSS, but many of the methods don't easily translate to React/Next.j ...

Modifying a section of the source name using jQuery continues to occur repeatedly when the window is resized

I have written a piece of code that is designed to identify the src attribute of all images within a specific div and modify the src name when the window width is less than 900 pixels. The code works perfectly when the page is refreshed, but I encounter an ...

Experiencing a "HEROES not found" error while following an Angular guide

I've been diving into Angular with the tutorial provided on https://angular.io. However, I've hit a roadblock at step 4. Displaying a list where I'm encountering an error in HeroesComponent. Cannot find name 'HEROES' The cod ...

Steps to retrieve a value from a promise function

My current challenge involves a function that verifies whether a device is online. Take a look at the code snippet below. const ping = require('ping'); export function checkDeviceStatus(device) { try { const hosts = [device]; let resul ...

Obtaining HTML data with ajax within a WordPress post

Greetings! I've been putting together a website and I'm eager to include a unique timeline in each of my posts. I'm utilizing WordPress for this project. However, since the timeline I want to insert will vary from post to post, I am unable t ...

Using the Presentational - Container (or Smart - Dumb) component approach in conjunction with Vuex

When it comes to managing the Presentational - Container (or Smart - Dumb) component pattern with Vuex, what is your recommended approach? Should the Presentational (or Dumb) components emit events to the parent or call Vuex actions? Imagine a scenario w ...

Content within a Row of a Data Table

Hello! I am just starting to learn JavaScript and jQuery. Can you help me with an issue I am experiencing? Basically, I have a table and I need to identify which tr contains a td with the text "Weekly", "Daily", or "Monthly". Once I locate that specific t ...

Converting Blob to File in Electron: A step-by-step guide

Is there a way to convert a Blob into a File object in ElectronJS? I attempted the following: return new File([blob], fileName, {lastModified: new Date().getTime(), type: blob.type}); However, it appears that ElectronJs handles the File object differently ...

Ways to verify the compatibility between TypeScript type definitions in @types and the corresponding package

After dabbling with typescript in my node.js projects for a while, I've come to realize that many npm packages have separate @types packages for typescript definitions. But here's the dilemma: how can one be certain that the @types package is syn ...

Error in AngularJS: The argument 'fn' is not a function and is undefined

I'm encountering an issue with AngularJS 1.6, and the error message is stating: Error: [ng:areq] Argument 'fn' is not a function, got undefined I suspect the problem lies within my testService, and I'm seeking assistance in identify ...

Having issues with Cypress testing of Material-UI datepicker on Github actions

Encountering an unusual issue while running Cypress tests in a GitHub action environment. The MUI datepicker is stuck in readonly mode, preventing any input of dates (works fine in other setups). Error displayed by Cypress CypressError: Timed out retryin ...