Skip to content

Checking States Examples ​

This page demonstrates various patterns for checking element states.

Single State Checks ​

Example 1: Check Visibility ​

typescript
async function checkVisibility(element: Element) {
  const result = await inspector.queryElementState(element, 'visible');
  
  console.log('Matches expected state:', result.matches);
  console.log('Actual state:', result.received); // 'visible' or 'hidden'
  
  return result.matches;
}

Example 2: Check Enabled/Disabled ​

typescript
async function checkEnabled(element: Element) {
  const result = await inspector.queryElementState(element, 'enabled');
  
  if (result.matches) {
    console.log('Element is enabled');
  } else {
    console.log(`Element is ${result.received}`); // 'disabled'
  }
  
  return result.matches;
}

Example 3: Check Viewport Position ​

typescript
async function checkViewportPosition(element: Element) {
  const result = await inspector.queryElementState(element, 'inview');
  
  console.log('Matches:', result.matches);
  console.log('State:', result.received);
  // Possible states: 'inview', 'notinview', 'unviewable'
  
  if (result.received === 'unviewable') {
    console.log('Element cannot be scrolled into view (hidden by overflow)');
  } else if (result.received === 'notinview') {
    console.log('Element can be scrolled into view');
  } else {
    console.log('Element is currently in viewport');
  }
}

Multiple State Checks ​

Example 4: Check Button Ready State ​

typescript
async function isButtonReady(button: Element) {
  const result = await inspector.queryElementStates(
    button,
    ['visible', 'enabled', 'stable']
  );
  
  if (result.status === 'success') {
    console.log('✓ Button is ready for interaction');
    return true;
  } else if (result.status === 'failure') {
    console.log(`✗ Button is not ready: ${result.missingState}`);
    return false;
  } else {
    console.log(`✗ Error checking button: ${result.message}`);
    return false;
  }
}

Example 5: Check Input Ready for Typing ​

typescript
async function isInputReady(input: Element) {
  const result = await inspector.queryElementStates(
    input,
    ['visible', 'enabled', 'editable']
  );
  
  switch (result.status) {
    case 'success':
      console.log('✓ Input is ready for typing');
      return true;
      
    case 'failure':
      console.log(`✗ Input is not ready: ${result.missingState}`);
      return false;
      
    case 'error':
      console.log(`✗ Error: ${result.message}`);
      return false;
  }
}

Example 6: Comprehensive Element Check ​

typescript
async function comprehensiveCheck(element: Element) {
  // Check all possible states
  const states = ['visible', 'enabled', 'stable', 'inview'] as const;
  const result = await inspector.queryElementStates(element, states);
  
  if (result.status === 'success') {
    console.log('✓ All states passed');
    return { ready: true };
  } else if (result.status === 'failure') {
    console.log(`✗ Missing state: ${result.missingState}`);
    return { ready: false, missingState: result.missingState };
  } else {
    console.log(`✗ Error: ${result.message}`);
    return { ready: false, error: result.message };
  }
}

Conditional State Checks ​

Example 7: Check States Based on Element Type ​

typescript
async function checkElementReady(element: Element) {
  const tagName = element.tagName.toLowerCase();
  
  let states: ElementState[];
  
  if (['input', 'textarea'].includes(tagName)) {
    // For inputs, check if editable
    states = ['visible', 'enabled', 'editable'];
  } else if (['button', 'a'].includes(tagName)) {
    // For buttons and links, just check if clickable
    states = ['visible', 'enabled'];
  } else {
    // For other elements, just check visibility
    states = ['visible'];
  }
  
  const result = await inspector.queryElementStates(element, states);
  
  return {
    ready: result.status === 'success',
    states,
    result
  };
}

Example 8: Progressive State Checking ​

typescript
async function progressiveCheck(element: Element) {
  // Check states one by one for detailed feedback
  
  console.log('Checking visibility...');
  const visibleResult = await inspector.queryElementState(element, 'visible');
  if (!visibleResult.matches) {
    return { ready: false, failedAt: 'visible' };
  }
  
  console.log('✓ Visible - Checking enabled...');
  const enabledResult = await inspector.queryElementState(element, 'enabled');
  if (!enabledResult.matches) {
    return { ready: false, failedAt: 'enabled' };
  }
  
  console.log('✓ Enabled - Checking viewport...');
  const inviewResult = await inspector.queryElementState(element, 'inview');
  if (!inviewResult.matches) {
    return { ready: false, failedAt: 'inview', state: inviewResult.received };
  }
  
  console.log('✓ In viewport - Checking stability...');
  const stableResult = await inspector.queryElementStates(element, ['stable']);
  if (stableResult.status !== 'success') {
    return { ready: false, failedAt: 'stable' };
  }
  
  console.log('✓ All checks passed');
  return { ready: true };
}

State Monitoring ​

Example 9: Monitor State Changes ​

typescript
async function monitorElementState(
  element: Element,
  duration: number = 5000
) {
  const start = Date.now();
  const history: Array<{ time: number; visible: boolean; enabled: boolean }> = [];
  
  while (Date.now() - start < duration) {
    const visible = inspector.isElementVisible(element);
    const enabled = !inspector.isElementDisabled(element);
    
    history.push({
      time: Date.now() - start,
      visible,
      enabled
    });
    
    await new Promise(resolve => setTimeout(resolve, 100));
  }
  
  console.log('State history:', history);
  
  // Analyze stability
  const changes = history.filter((state, i) => {
    if (i === 0) return false;
    const prev = history[i - 1];
    return state.visible !== prev.visible || state.enabled !== prev.enabled;
  });
  
  console.log(`State changed ${changes.length} times in ${duration}ms`);
  
  return { history, changes };
}

Example 10: Wait for State Change ​

typescript
async function waitForStateChange(
  element: Element,
  expectedState: 'visible' | 'hidden',
  timeout: number = 5000
) {
  const start = Date.now();
  
  while (Date.now() - start < timeout) {
    const result = await inspector.queryElementState(element, expectedState);
    
    if (result.matches) {
      const elapsed = Date.now() - start;
      console.log(`✓ Element became ${expectedState} after ${elapsed}ms`);
      return true;
    }
    
    await new Promise(resolve => setTimeout(resolve, 100));
  }
  
  console.log(`✗ Element did not become ${expectedState} within ${timeout}ms`);
  return false;
}

// Usage
await waitForStateChange(element, 'visible', 3000);

Batch State Checks ​

Example 11: Check Multiple Elements' States ​

typescript
async function batchStateCheck(
  elements: Element[],
  requiredStates: ElementState[]
) {
  const results = await Promise.all(
    elements.map(async (element, index) => {
      const result = await inspector.queryElementStates(element, requiredStates);
      
      return {
        index,
        element,
        ready: result.status === 'success',
        missingState: result.status === 'failure' ? result.missingState : null,
        error: result.status === 'error' ? result.message : null
      };
    })
  );
  
  const readyCount = results.filter(r => r.ready).length;
  console.log(`${readyCount}/${elements.length} elements are ready`);
  
  return results;
}

// Usage
const buttons = Array.from(document.querySelectorAll('.action-button'));
const results = await batchStateCheck(buttons, ['visible', 'enabled']);

Example 12: Find First Ready Element ​

typescript
async function findFirstReady(
  selectors: string[],
  requiredStates: ElementState[]
): Promise<Element | null> {
  for (const selector of selectors) {
    const element = document.querySelector(selector);
    if (!element) continue;
    
    const result = await inspector.queryElementStates(element, requiredStates);
    
    if (result.status === 'success') {
      console.log(`✓ Found ready element: ${selector}`);
      return element;
    }
  }
  
  console.log('✗ No ready elements found');
  return null;
}

// Usage
const button = await findFirstReady(
  ['#primary-button', '#secondary-button', '#fallback-button'],
  ['visible', 'enabled']
);

Error Handling ​

Example 13: Robust State Checking ​

typescript
async function robustStateCheck(element: Element) {
  try {
    const result = await inspector.queryElementStates(
      element,
      ['visible', 'enabled', 'stable']
    );
    
    if (result.status === 'success') {
      return { success: true };
    } else if (result.status === 'failure') {
      return {
        success: false,
        reason: 'missing_state',
        missingState: result.missingState
      };
    } else {
      return {
        success: false,
        reason: 'error',
        message: result.message
      };
    }
  } catch (error) {
    console.error('Unexpected error during state check:', error);
    return {
      success: false,
      reason: 'exception',
      error
    };
  }
}

Example 14: Validate Editable State Safely ​

Querying editable on an element that cannot be edited does not throw: received is 'error:noteditable'. This includes elements that are not <input>, <textarea>, <select>, or editable, unless their role allows aria-readonly, such as textbox or combobox.

typescript
async function safeEditableCheck(element: Element) {
  const result = await inspector.queryElementState(element, 'editable');

  if (result.received === 'error:noteditable') {
    return {
      editable: false,
      reason: 'Element type does not support editable state'
    };
  }

  return {
    editable: result.matches,
    state: result.received // 'editable', 'disabled', 'readOnly', or 'error:notconnected'
  };
}

Checked States ​

Example 15: Check a Checkbox or Radio Button ​

typescript
async function getCheckedState(element: Element) {
  const result = await inspector.queryElementState(element, 'checked');

  if (result.received === 'error:notcheckable') {
    return { checkable: false };
  }

  return {
    checkable: true,
    checked: result.matches,
    state: result.received, // 'checked', 'unchecked', or 'indeterminate'
    isRadio: result.isRadio // A radio button cannot be unchecked by clicking it
  };
}

const selectAll = document.querySelector('#select-all');
const { state } = await getCheckedState(selectAll);
if (state === 'indeterminate') {
  console.log('Some, but not all, items are selected');
}

Text, Label, and ARIA State Lookups ​

Example 16: Find Elements by Text, Label, and ARIA State ​

typescript
// Elements whose text contains a string, ignoring case
const rows = Array.from(document.querySelectorAll('tr'));
const containsText = inspector.elementsContainText(rows, 'overdue');
const overdueRows = rows.filter((_, index) => containsText[index]);

// Elements by label: aria-labelledby, aria-label, or label elements
const [emailInput] = inspector.findElementsByLabel([document], 'Email', false);
console.log(inspector.getElementLabels(emailInput)); // ['Email']

// Elements with ARIA states
const tabs = Array.from(document.querySelectorAll('[role="tab"]'));
const selected = inspector.elementsMatchAriaStates(tabs, { selected: true });
const selectedTab = tabs.find((_, index) => selected[index]);

// Include open shadow roots in a search
const scopes = [document, ...inspector.findOpenShadowRoots([document])];
const buttons = scopes.flatMap((scope) => Array.from(scope.querySelectorAll('button')));

Next Steps ​

Released under the Apache License 2.0.