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

6.6 KiB

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:

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:

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:

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:

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:

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.