# 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--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.