Files
Plataforma-de-desarrolladores/AGENTS_JAVASCRIPT_INITIALIZATION.md
Jorge Luis caudillo esparza 6685ef63fb Initial commit
2026-07-14 11:15:57 -07:00

165 lines
6.6 KiB
Markdown

# Reglas obligatorias para inicialización JavaScript idempotente
Estas reglas complementan `AGENTS.md` y `AGENTS_WEBFORMS_PARTIAL_POSTBACKS.md`.
## Objetivo
Los inicializadores deben poder ejecutarse después de la carga inicial y después de cualquier postback parcial sin:
- inicializar dos veces el mismo control;
- duplicar eventos;
- destruir y reconstruir controles que siguen vigentes;
- modificar elementos que ya fueron procesados;
- producir errores porque un plugin ya existe.
## Banderas por elemento
- Cada control inicializado debe conservar una bandera en el propio elemento HTML.
- Los inicializadores deben seleccionar únicamente elementos que todavía no tengan su bandera.
- Para código nuevo, usar atributos con namespace `data-sig-<componente>-initialized="1"`.
- La bandera debe identificar el componente, no usar nombres genéricos que puedan colisionar.
Ejemplos:
```html
data-sig-select2-initialized="1"
data-sig-tooltip-initialized="1"
data-sig-dropzone-initialized="1"
data-sig-datepicker-initialized="1"
```
- `custom.js` ya contiene banderas heredadas como `loaded`, `creado` y `ADsortable`. Deben respetarse mientras sus inicializadores sigan activos.
- Al modificar completamente uno de esos inicializadores, puede migrarse a `data-sig-*`, pero deben actualizarse juntos todos sus selectores, lecturas, escrituras y rutinas de destrucción.
- Para banderas nuevas visibles en el DOM, preferir `.attr('data-sig-...', '1')`, `.is('[data-sig-...]')` o `element.dataset`.
- No mezclar `.data()` y `.attr()` para leer/escribir la misma bandera: jQuery mantiene un caché interno y ambos valores pueden quedar diferentes.
## Momento correcto para colocar la bandera
- Comprobar la bandera antes de modificar el elemento o registrar eventos.
- Colocar la bandera sólo después de que el plugin o control termine de inicializar correctamente.
- Si la inicialización falla, el elemento debe permanecer sin bandera para permitir un reintento posterior.
- No colocar la bandera antes de una operación que pueda lanzar una excepción.
Ejemplo:
```javascript
function initializeSelect2(root) {
var $root = $(root || document);
$root.find('.js-select2:not([data-sig-select2-initialized="1"])')
.addBack('.js-select2:not([data-sig-select2-initialized="1"])')
.each(function () {
var $control = $(this);
$control.select2({
width: '100%'
});
$control.attr('data-sig-select2-initialized', '1');
});
}
```
## Postbacks parciales y contenido nuevo
- Cuando un `UpdatePanel` reemplaza su HTML, los elementos nuevos no contienen la bandera de la instancia anterior y serán inicializados normalmente.
- Los elementos fuera del panel actualizado conservan su bandera y deben ignorarse.
- Siempre que sea posible, pasar como `root` únicamente el panel actualizado en lugar de volver a recorrer todo `document`.
- Centralizar la conexión con `Sys.WebForms.PageRequestManager.endRequest` o `Sys.Application.load`; no registrar ese puente desde cada página.
- El puente global debe registrarse una sola vez.
Ejemplo:
```javascript
function initializeSigControls(root) {
initializeSelect2(root);
initializeTooltips(root);
initializeDatePickers(root);
}
if (window.SIG_PARTIAL_INITIALIZERS_REGISTERED !== true) {
window.SIG_PARTIAL_INITIALIZERS_REGISTERED = true;
Sys.WebForms.PageRequestManager.getInstance().add_endRequest(
function (sender, args) {
var panels = args.get_panelsUpdated();
if (!panels || panels.length === 0) {
initializeSigControls(document);
return;
}
for (var index = 0; index < panels.length; index++) {
initializeSigControls(panels[index]);
}
}
);
}
```
## Eventos delegados y eventos directos
- Los eventos delegados sobre `body`, `document` o un contenedor permanente deben registrarse una sola vez mediante una bandera global.
- `custom.js` ya usa `window.SIG_CUSTOM_READY_INITIALIZED`; conservar este patrón para el bloque global existente.
- No ejecutar repetidamente `.off().on()` sobre todos los controles después de cada postback parcial.
- Para eventos directos inevitables, inicializar únicamente elementos sin bandera y usar eventos con namespace.
Ejemplo de evento directo:
```javascript
function initializeAmountInputs(root) {
$(root || document)
.find('.js-amount:not([data-sig-amount-initialized="1"])')
.each(function () {
var $input = $(this);
$input.on('change.sigAmount', function () {
// Manejar cambio.
});
$input.attr('data-sig-amount-initialized', '1');
});
}
```
## Destrucción y reinicialización explícita
- No quitar una bandera sólo para forzar que el inicializador vuelva a ejecutarse.
- Quitar la bandera únicamente cuando exista una necesidad real de destruir o reconfigurar el componente.
- Antes de quitarla, llamar la API de destrucción del plugin y eliminar eventos con su namespace.
- El orden obligatorio es: destruir plugin, retirar eventos propios, quitar bandera y finalmente reinicializar si corresponde.
Ejemplo:
```javascript
function destroySelect2(control) {
var $control = $(control);
if ($control.attr('data-sig-select2-initialized') !== '1') {
return;
}
if ($control.hasClass('select2-hidden-accessible')) {
$control.select2('destroy');
}
$control.off('.sigSelect2');
$control.removeAttr('data-sig-select2-initialized');
}
```
## Clonado y reemplazo de controles
- No copiar accidentalmente banderas de inicialización al clonar controles.
- Preferir clonar la plantilla antes de inicializarla.
- Si se clona un elemento ya inicializado, retirar del clon las banderas `data-sig-*-initialized`, IDs generados por plugins y contenedores auxiliares antes de insertarlo.
- Un control eliminado por un `UpdatePanel` no necesita limpieza manual de bandera, pero los recursos globales o listeners externos sí deben liberarse cuando el plugin lo requiera.
## Organización
- Cada inicializador debe ser una función pequeña e idempotente que acepte un elemento raíz.
- La función central sólo coordina inicializadores; no debe contener toda la lógica de cada plugin.
- No duplicar inicializadores dentro de páginas `.aspx` si pertenecen globalmente a `Scripts/custom.js`.
- El JavaScript específico de una página puede vivir en el placeholder `J`, pero debe seguir las mismas reglas de banderas y postbacks parciales.
- No modificar archivos minificados ni dependencias de terceros para implementar estas banderas.