---
sidebar_position: 6
---
# Best Practices

Guidelines for building maintainable, performant, and accessible Myop components.

## Code Organization

### 1. Use IIFE Encapsulation

Keep all implementation private. Only expose the public API:

```javascript
(function() {
    // ===== PRIVATE =====
    let state = {};
    const cache = {};

    function privateHelper() { /* ... */ }
    function render() { /* ... */ }

    // ===== PUBLIC API =====
    window.myop_init_interface = function(data) { /* ... */ };
    window.myop_cta_handler = function(action, payload) { /* ... */ };
})();
```

### 2. Cache DOM References

Query the DOM once during initialization:

```javascript
// Good - cached references
const elements = {
    appRoot: document.getElementById('app-root'),
    loader: document.getElementById('loader-container'),
    list: document.querySelector('.item-list'),
    searchInput: document.querySelector('.search-input'),
    filterPanel: document.querySelector('.filter-panel')
};

function render() {
    elements.list.innerHTML = renderItems();
}

// Bad - repeated queries
function render() {
    document.querySelector('.item-list').innerHTML = renderItems();
}
```

### 3. Single Initialization Flag

Attach event listeners only once:

```javascript
let isInitialized = false;

function initializeComponent(data) {
    // Process data...
    render();

    // Attach listeners only on first init
    if (!isInitialized) {
        attachEventListeners();
        isInitialized = true;
    }

    hideLoader();
}
```

## Event Handling

### 1. Use Event Delegation

For dynamic content, attach listeners to stable parent elements:

```javascript
// Good - event delegation
elements.list.addEventListener('click', (e) => {
    const item = e.target.closest('.item');
    if (item) {
        handleItemClick(item.dataset.id);
    }

    const deleteBtn = e.target.closest('.delete-btn');
    if (deleteBtn) {
        handleDelete(deleteBtn.dataset.id);
    }
});

// Bad - attaching to each element
items.forEach(item => {
    item.addEventListener('click', () => handleItemClick(item.id));
});
```

### 2. Debounce Expensive Operations

```javascript
function debounce(fn, delay) {
    let timeoutId;
    return function(...args) {
        clearTimeout(timeoutId);
        timeoutId = setTimeout(() => fn.apply(this, args), delay);
    };
}

const debouncedSearch = debounce((query) => {
    filterComponents(query);
    render();
}, 300);

elements.searchInput.addEventListener('input', (e) => {
    debouncedSearch(e.target.value);
});
```

## Data Handling

### 1. Parse Dates Once

Transform date strings during initialization, not during render:

```javascript
// Good - parse once
function initializeComponent(data) {
    state.components = data.components.map(c => ({
        ...c,
        lastEditedDate: new Date(c.lastEditedDate)
    }));
}

function formatDate(date) {
    return date.toLocaleDateString(); // Already a Date object
}

// Bad - parse on every render
function renderItem(item) {
    const date = new Date(item.lastEditedDate); // Parsed every time
    return `<div>${date.toLocaleDateString()}</div>`;
}
```

### 2. Provide Fallback Values

Handle missing or malformed data:

```javascript
function normalizeComponent(raw) {
    return {
        id: raw.id || `temp-${Date.now()}`,
        name: raw.name || 'Unnamed Component',
        description: raw.description || '',
        tags: Array.isArray(raw.tags) ? raw.tags : [],
        environments: Array.isArray(raw.environments) ? raw.environments : [],
        developerName: raw.developerName || 'Unknown',
        lastEditedDate: raw.lastEditedDate
            ? new Date(raw.lastEditedDate)
            : new Date()
    };
}
```

### 3. Immutable Updates

Don't mutate the original state:

```javascript
// Good - immutable update
function updateComponent(updated) {
    state.components = state.components.map(c =>
        c.id === updated.id ? { ...c, ...updated } : c
    );
    render();
}

// Bad - mutation
function updateComponent(updated) {
    const index = state.components.findIndex(c => c.id === updated.id);
    state.components[index] = { ...state.components[index], ...updated }; // Mutates array
    render();
}
```

## Performance

### 1. Minimize DOM Operations

Batch DOM updates:

```javascript
// Good - single innerHTML update
function render() {
    elements.list.innerHTML = state.components
        .map(renderItem)
        .join('');
}

// Bad - multiple appendChild calls
function render() {
    elements.list.innerHTML = '';
    state.components.forEach(comp => {
        const div = document.createElement('div');
        div.innerHTML = renderItem(comp);
        elements.list.appendChild(div); // Triggers reflow each time
    });
}
```

### 2. Use Document Fragments for Large Lists

```javascript
function render() {
    const fragment = document.createDocumentFragment();

    state.components.forEach(comp => {
        const div = document.createElement('div');
        div.className = 'item';
        div.innerHTML = renderItemContent(comp);
        fragment.appendChild(div);
    });

    elements.list.innerHTML = '';
    elements.list.appendChild(fragment);
}
```

### 3. Virtual Scrolling for Large Datasets

For lists with 100+ items, consider virtual scrolling:

```javascript
const ITEM_HEIGHT = 60;
const VISIBLE_ITEMS = 20;
const BUFFER = 5;

function renderVisibleItems(scrollTop) {
    const startIndex = Math.max(0, Math.floor(scrollTop / ITEM_HEIGHT) - BUFFER);
    const endIndex = startIndex + VISIBLE_ITEMS + (BUFFER * 2);

    const visibleItems = state.components.slice(startIndex, endIndex);

    elements.list.style.paddingTop = `${startIndex * ITEM_HEIGHT}px`;
    elements.list.style.height = `${state.components.length * ITEM_HEIGHT}px`;
    elements.list.innerHTML = visibleItems.map(renderItem).join('');
}

elements.scrollContainer.addEventListener('scroll', (e) => {
    requestAnimationFrame(() => {
        renderVisibleItems(e.target.scrollTop);
    });
});
```

## Security

### 1. Escape User Content

Always escape HTML in user-provided content:

```javascript
function escapeHtml(text) {
    const div = document.createElement('div');
    div.textContent = text;
    return div.innerHTML;
}

function renderItem(item) {
    return `
        <div class="item">
            <h3>${escapeHtml(item.name)}</h3>
            <p>${escapeHtml(item.description)}</p>
        </div>
    `;
}
```

### 2. Validate Data Attributes

Use data attributes safely:

```javascript
// Good - validate before use
const itemId = e.target.dataset.id;
if (itemId && /^[a-zA-Z0-9-]+$/.test(itemId)) {
    handleItemClick(itemId);
}

// Bad - trusting user input
const itemId = e.target.dataset.id;
document.querySelector(`[data-id="${itemId}"]`); // XSS risk
```

### 3. Sanitize URLs

```javascript
function createLink(url, text) {
    // Only allow http/https URLs
    if (!/^https?:\/\//i.test(url)) {
        return escapeHtml(text);
    }
    return `<a href="${escapeHtml(url)}" rel="noopener noreferrer">${escapeHtml(text)}</a>`;
}
```

## Accessibility

### 1. Use Semantic HTML

```html
<!-- Good -->
<nav aria-label="Component filters">
    <ul role="listbox">
        <li role="option" aria-selected="true">All</li>
        <li role="option">Frontend</li>
    </ul>
</nav>

<main>
    <ul class="component-list" role="list">
        <li class="item" role="listitem">...</li>
    </ul>
</main>

<!-- Bad -->
<div class="nav">
    <div class="filter">All</div>
    <div class="filter">Frontend</div>
</div>
<div class="list">
    <div class="item">...</div>
</div>
```

### 2. Add ARIA Labels

```javascript
function renderItem(item) {
    return `
        <article
            class="item"
            aria-label="${escapeHtml(item.name)}"
            tabindex="0"
        >
            <h3>${escapeHtml(item.name)}</h3>
            <button
                class="delete-btn"
                aria-label="Delete ${escapeHtml(item.name)}"
                data-id="${item.id}"
            >
                Delete
            </button>
        </article>
    `;
}
```

### 3. Support Keyboard Navigation

```javascript
elements.list.addEventListener('keydown', (e) => {
    const item = e.target.closest('.item');
    if (!item) return;

    switch (e.key) {
        case 'Enter':
        case ' ':
            e.preventDefault();
            handleItemClick(item.dataset.id);
            break;
        case 'Delete':
        case 'Backspace':
            e.preventDefault();
            handleDelete(item.dataset.id);
            break;
        case 'ArrowDown':
            e.preventDefault();
            item.nextElementSibling?.focus();
            break;
        case 'ArrowUp':
            e.preventDefault();
            item.previousElementSibling?.focus();
            break;
    }
});
```

## Error Handling

### 1. Graceful Degradation

```javascript
function initializeComponent(data) {
    try {
        if (!data || !Array.isArray(data.components)) {
            throw new Error('Invalid data format');
        }

        state.components = data.components.map(normalizeComponent);
        render();
        hideLoader();
    } catch (error) {
        console.error('Failed to initialize:', error);
        showErrorState(error.message);
    }
}

function showErrorState(message) {
    elements.loader.style.display = 'none';
    elements.appRoot.innerHTML = `
        <div class="error-state" role="alert">
            <h2>Unable to load components</h2>
            <p>${escapeHtml(message)}</p>
            <button onclick="window.myop_cta_handler('retry')">
                Try Again
            </button>
        </div>
    `;
    elements.appRoot.style.opacity = '1';
}
```

### 2. Safe CTA Handler

```javascript
window.myop_cta_handler = function(action_id, payload) {
    try {
        // Validate action
        if (typeof action_id !== 'string') {
            console.warn('Invalid action_id type');
            return;
        }

        console.log('CTA:', action_id, payload);
    } catch (error) {
        console.error('CTA handler error:', error);
    }
};
```

## Summary Checklist

| Category | Practice |
|----------|----------|
| **Structure** | Use IIFE pattern |
| **Structure** | Cache DOM references |
| **Structure** | Initialize once flag |
| **Events** | Event delegation |
| **Events** | Debounce expensive operations |
| **Data** | Parse dates once |
| **Data** | Provide fallbacks |
| **Data** | Immutable updates |
| **Performance** | Batch DOM updates |
| **Performance** | Use fragments for large lists |
| **Security** | Escape HTML |
| **Security** | Validate data attributes |
| **Security** | Sanitize URLs |
| **Accessibility** | Semantic HTML |
| **Accessibility** | ARIA labels |
| **Accessibility** | Keyboard navigation |
| **Errors** | Graceful degradation |
| **Errors** | Safe handlers |

## Next Steps

- Review [Component Structure](/docs/learnMyop/html-components/component-structure) for the complete pattern
- Learn about [Data Loading](/docs/learnMyop/html-components/data-loading) lifecycle
- See [Host Integration](/docs/learnMyop/html-components/host-integration) examples
