Element Events
Elements emit events that allow your application to respond to user interactions and state changes. This document explains how to effectively work with Element events.
Quick Reference
Event | Description | Common Use Cases |
---|---|---|
ready | Triggered when the Element is mounted and ready | Initialize UI state, show the form after Elements are ready |
focus | Triggered when the Element receives focus | Apply visual focus styles, show help text |
blur | Triggered when the Element loses focus | Validate input, hide help text |
change | Triggered when the Element's value changes | Update UI based on input state, enable/disable submit buttons |
keydown | Triggered when a key is pressed while the Element has focus | Handle special key actions (Enter to submit, etc.) |
Handling Events
- Web Elements
- React Elements
// Create a card element with inline event handlers
const cardElement = bt.createElement('card', {
targetId: 'my-card',
onReady: () => {
console.log('Card element is ready');
showForm(); // Show the form once Element is ready
},
onChange: (event) => {
// Update UI based on card input state
updateSubmitButton(event.complete);
if (event.error) {
showError(event.error.message);
} else {
clearError();
}
}
});
// Mount the element
cardElement.mount('#card-container');
// You can also add event listeners after creation
cardElement.on('focus', () => {
highlightCardField(); // Apply visual focus styles
showCardHelpText(); // Show help text when field is focused
});
cardElement.on('blur', () => {
resetCardFieldHighlight();
hideCardHelpText();
});
// Add keydown handler to submit form on Enter key
cardElement.on('keydown', (event) => {
if (event.key === 'Enter' && cardElement.getState().complete) {
document.getElementById('payment-form').requestSubmit();
}
});
// Remove event listeners when no longer needed
const changeHandler = (event) => {
// Handle change events
};
cardElement.on('change', changeHandler); // Add listener
import { useState, useEffect } from 'react';
import {
CardElement,
useBasisTheory
} from '@basis-theory/react-elements';
function PaymentForm() {
const { bt } = useBasisTheory('<PUBLIC_API_KEY>');
const [isReady, setIsReady] = useState(false);
const [isComplete, setIsComplete] = useState(false);
const [isFocused, setIsFocused] = useState(false);
const [errorMessage, setErrorMessage] = useState('');
// Element event handlers
const handleReady = () => {
console.log('Card element is ready');
setIsReady(true);
};
const handleChange = (event) => {
console.log('Card element changed:', event);
setIsComplete(event.complete);
if (event.error) {
setErrorMessage(event.error.message);
} else {
setErrorMessage('');
}
if (event.cardBrand) {
console.log('Card brand detected:', event.cardBrand);
}
};
const handleFocus = () => {
console.log('Card element focused');
setIsFocused(true);
};
const handleBlur = () => {
console.log('Card element blurred');
setIsFocused(false);
};
const handleKeyDown = (event) => {
console.log('Key pressed:', event.key);
// Submit form on Enter key if input is complete
if (event.key === 'Enter' && isComplete) {
handleSubmit();
}
};
const handleSubmit = async () => {
// Tokenization logic
};
return (
<div className={`payment-form ${!isReady ? 'loading' : ''}`}>
{!isReady && <div className="loader">Loading payment form...</div>}
<form onSubmit={(e) => { e.preventDefault(); handleSubmit(); }}>
<div className={`form-group ${isFocused ? 'focused' : ''}`}>
<label htmlFor="card-element">Card details</label>
<CardElement
id="card-element"
onReady={handleReady}
onChange={handleChange}
onFocus={handleFocus}
onBlur={handleBlur}
onKeyDown={handleKeyDown}
/>
{errorMessage && (
<div className="error-message">{errorMessage}</div>
)}
{isFocused && (
<div className="help-text">
Enter your card details as they appear on your card.
</div>
)}
</div>
<button
type="submit"
disabled={!isComplete}
className={isComplete ? 'btn-primary' : 'btn-disabled'}
>
Pay Now
</button>
</form>
</div>
);
}
Event Data Reference
Ready Event
The ready
event is fired when the Element has been mounted and is ready for user interaction.
// Event data
{
type: 'ready'
}
Change Event
The change
event is fired when the Element's value changes. The data varies by element type.
Base Change Event Properties (All Elements)
{
complete: false, // Whether the input is complete/valid
empty: true, // Whether the input is empty
errors: FieldError[], // Array of FieldErrors
maskSatisfied: false, // Whether the mask, if provided, is satisfied
type: "change"
valid: false, // Whether the current input is valid
}
Card Element Change Event Properties
{
cardBin: undefined, // First 6 to 8 digits of a credit or debit card number
cardBrand: 'visa', // Detected card brand (visa, mastercard, amex, etc.)
cardLast4: undefined, // Last 4 digits of a credit or debit card number
complete: true, // Whether all required card fields are complete
empty: true, // Whether the input is empty
errors: FieldError[], // Array of FieldErrors
maskSatisfied: false, // Whether the mask, if provided, is satisfied
valid: false // Whether the current input is valid
}
Card Number Element Change Event Properties
{
cardBin: undefined, // First 6 to 8 digits of a credit or debit card number
cardBrand: 'visa', // Detected card brand (visa, mastercard, amex, etc.)
cardLast4: undefined, // Last 4 digits of a credit or debit card number
complete: true, // Whether all required card fields are complete
empty: true, // Whether the input is empty
errors: FieldError[], // Array of FieldErrors
maskSatisfied: false, // Whether the mask, if provided, is satisfied
valid: false, // Whether the current input is valid
}
Expiration Date Element Change Event Properties
{
complete: true, // Whether all required card fields are complete
empty: true, // Whether the input is empty
errors: FieldError[], // Array of FieldErrors
maskSatisfied: false, // Whether the mask, if provided, is satisfied
valid: false, // Whether the current input is valid
}
Card Verification Code Element Change Event Properties
{
complete: true, // Whether all required card fields are complete
empty: true, // Whether the input is empty
errors: FieldError[], // Array of FieldErrors
maskSatisfied: false, // Whether the mask, if provided, is satisfied
valid: false, // Whether the current input is valid
}
Focus Events
{
targetId: string,
type: 'focus',
}
Blur Events
{
complete: true, // Whether all required card fields are complete
empty: true, // Whether the input is empty
errors: FieldError[], // Array of FieldErrors
maskSatisfied: false, // Whether the mask, if provided, is satisfied
targetId: string, // Elements targetId
type: "blur",
valid: false, // Whether the current input is valid
}
Keydown Event
The keydown
event includes information about the key that was pressed. It is triggered when user hits a special key inside an element input.
{
altKey: false,
ctrlKey: false,
key: "Enter",
metaKey: false,
shiftKey: false,
targetId: string,
}
Errors
type FieldError = {
type: "incomplete" | "invalid",
targetId: string
}
Best Practices for Event Handling
Form Validation with Events
- Web Elements
- React Elements
// Track the state of multiple elements
const formState = {
cardNumber: { complete: false },
cardExpiry: { complete: false },
cardCvc: { complete: false }
};
// Update form state when each element changes
cardNumberElement.on('change', (event) => {
formState.cardNumber = {
complete: event.complete,
valid: event.valid,
cardBrand: event.cardBrand
};
updateFormState();
});
cardExpiryElement.on('change', (event) => {
formState.cardExpiry = {
complete: event.complete,
valid: event.valid
};
updateFormState();
});
cardCvcElement.on('change', (event) => {
formState.cardCvc = {
complete: event.complete,
valid: event.valid
};
updateFormState();
});
// Update the UI based on overall form state
function updateFormState() {
const submitButton = document.getElementById('submit-button');
const isFormComplete =
formState.cardNumber.complete &&
formState.cardExpiry.complete &&
formState.cardCvc.complete;
submitButton.disabled = !isFormComplete;
// Show the card brand icon if detected
if (formState.cardNumber.cardBrand) {
showCardBrandIcon(formState.cardNumber.cardBrand);
}
}
import { useState, useRef } from 'react';
import {
CardNumberElement,
CardExpirationDateElement,
CardVerificationCodeElement,
useBasisTheory
} from '@basis-theory/react-elements';
function PaymentForm() {
const { bt } = useBasisTheory();
const [cardBrand, setCardBrand] = useState(null);
const [formState, setFormState] = useState({
cardNumber: { complete: false, valid: false },
cardExpiry: { complete: false, valid: false },
cardCvc: { complete: false, valid: false }
});
// Element refs for tokenization
const cardNumberRef = useRef(null);
const cardExpiryRef = useRef(null);
const cardCvcRef = useRef(null);
// Calculate if the form is ready to submit
const isFormComplete =
formState.cardNumber.complete &&
formState.cardExpiry.complete &&
formState.cardCvc.complete;
// Handle card number changes
const handleCardNumberChange = (event) => {
setFormState(prevState => ({
...prevState,
cardNumber: {
complete: event.complete,
valid: event.valid
}
}));
if (event.cardBrand) {
setCardBrand(event.cardBrand);
}
};
// Handle expiry date changes
const handleExpiryChange = (event) => {
setFormState(prevState => ({
...prevState,
cardExpiry: {
complete: event.complete,
valid: event.valid
}
}));
};
// Handle CVC changes
const handleCvcChange = (event) => {
setFormState(prevState => ({
...prevState,
cardCvc: {
complete: event.complete,
valid: event.valid
}
}));
};
// Handle form submission
const handleSubmit = async (e) => {
e.preventDefault();
if (!isFormComplete) return;
try {
const { token } = await bt.tokenize({
card: {
number: cardNumberRef.current,
expiration_date: cardExpiryRef.current,
cvc: cardCvcRef.current
}
});
console.log('Token created:', token.id);
} catch (error) {
console.error('Tokenization failed:', error);
}
};
return (
<form onSubmit={handleSubmit}>
<div className="form-row">
<label>Card Number</label>
<CardNumberElement
id="card-number"
ref={cardNumberRef}
onChange={handleCardNumberChange}
/>
{cardBrand && <div className="card-brand-icon">{cardBrand}</div>}
</div>
<div className="form-row-split">
<div className="form-col">
<label>Expiration</label>
<CardExpirationDateElement
id="card-expiry"
ref={cardExpiryRef}
onChange={handleExpiryChange}
/>
</div>
<div className="form-col">
<label>CVC</label>
<CardVerificationCodeElement
id="card-cvc"
ref={cardCvcRef}
cardBrand={cardBrand}
onChange={handleCvcChange}
/>
</div>
</div>
<button
type="submit"
disabled={!isFormComplete}
className={isFormComplete ? 'btn-primary' : 'btn-disabled'}
>
Submit Payment
</button>
</form>
);
}
Loading States with the Ready Event
- Web Elements
- React Elements
// Show a loading state initially
const formContainer = document.getElementById('payment-form-container');
const loadingIndicator = document.getElementById('loading-indicator');
// Hide the form, show loading indicator
formContainer.style.display = 'none';
loadingIndicator.style.display = 'block';
// Track ready state of multiple elements
let elementsReady = {
cardNumber: false,
cardExpiry: false,
cardCvc: false
};
// Create and mount elements
const cardNumberElement = bt.createElement('cardNumber', {
targetId: 'card-number',
onReady: () => {
elementsReady.cardNumber = true;
checkAllElementsReady();
}
});
const cardExpiryElement = bt.createElement('cardExpirationDate', {
targetId: 'card-expiry',
onReady: () => {
elementsReady.cardExpiry = true;
checkAllElementsReady();
}
});
const cardCvcElement = bt.createElement('cardVerificationCode', {
targetId: 'card-cvc',
onReady: () => {
elementsReady.cardCvc = true;
checkAllElementsReady();
}
});
// Mount all elements
cardNumberElement.mount('#card-number-container');
cardExpiryElement.mount('#card-expiry-container');
cardCvcElement.mount('#card-cvc-container');
// Check if all elements are ready
function checkAllElementsReady() {
const allReady =
elementsReady.cardNumber &&
elementsReady.cardExpiry &&
elementsReady.cardCvc;
if (allReady) {
// Hide loading indicator, show the form
loadingIndicator.style.display = 'none';
formContainer.style.display = 'block';
}
}
import { useState } from 'react';
import {
CardNumberElement,
CardExpirationDateElement,
CardVerificationCodeElement,
useBasisTheory
} from '@basis-theory/react-elements';
function PaymentForm() {
const { bt } = useBasisTheory();
// Track ready state of all elements
const [elementsReady, setElementsReady] = useState({
cardNumber: false,
cardExpiry: false,
cardCvc: false
});
// Check if all elements are ready
const allElementsReady =
elementsReady.cardNumber &&
elementsReady.cardExpiry &&
elementsReady.cardCvc;
// Handle ready events for each element
const handleCardNumberReady = () => {
setElementsReady(prev => ({ ...prev, cardNumber: true }));
};
const handleCardExpiryReady = () => {
setElementsReady(prev => ({ ...prev, cardExpiry: true }));
};
const handleCardCvcReady = () => {
setElementsReady(prev => ({ ...prev, cardCvc: true }));
};
return (
<div className="payment-form-container">
{!allElementsReady && (
<div className="loading-indicator">
<div className="spinner"></div>
<p>Loading secure payment form...</p>
</div>
)}
<div className={`payment-form ${allElementsReady ? 'visible' : 'hidden'}`}>
<h3>Enter Payment Details</h3>
<div className="form-row">
<label>Card Number</label>
<CardNumberElement
id="card-number"
onReady={handleCardNumberReady}
/>
</div>
<div className="form-row-split">
<div className="form-col">
<label>Expiration</label>
<CardExpirationDateElement
id="card-expiry"
onReady={handleCardExpiryReady}
/>
</div>
<div className="form-col">
<label>CVC</label>
<CardVerificationCodeElement
id="card-cvc"
onReady={handleCardCvcReady}
/>
</div>
</div>
<button type="submit">Pay Now</button>
</div>
</div>
);
}
Troubleshooting Event Issues
See the Troubleshooting Guide for help with event-related issues, including a complete debugging strategy.