{"id":297,"date":"2026-06-17T18:27:26","date_gmt":"2026-06-18T01:27:26","guid":{"rendered":"https:\/\/www.mariatech.com.mx\/blog\/?p=297"},"modified":"2026-06-17T18:27:27","modified_gmt":"2026-06-18T01:27:27","slug":"como-corregir-el-bug-de-scroll-causado-por-iconify-e-intersectionobserver-v2-1-0","status":"publish","type":"post","link":"https:\/\/www.mariatech.com.mx\/blog\/plugins\/como-corregir-el-bug-de-scroll-causado-por-iconify-e-intersectionobserver-v2-1-0\/","title":{"rendered":"C\u00f3mo corregir el bug de scroll causado por Iconify e IntersectionObserver v2.1.0"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">En interfaces administrativas con muchos iconos, formularios, tablas y modales, puede aparecer un comportamiento extra\u00f1o: al hacer scroll en p\u00e1ginas extensas, la pantalla se congela por momentos o parece \u201crebotar\u201d. Aunque visualmente se percibe como un salto, normalmente el problema viene de un ciclo de recalculado del navegador provocado por alg\u00fan script que modifica el DOM mientras el usuario se desplaza.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">En este caso, el origen del problema estaba relacionado con <strong>Iconify<\/strong>, espec\u00edficamente con el archivo <code>iconify-icon.js<\/code> y el uso de <code>IntersectionObserver<\/code> para renderizar o remover iconos dependiendo de si est\u00e1n visibles en pantalla.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">El s\u00edntoma<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">El problema aparece \u00fanicamente en p\u00e1ginas con altura considerable, donde era necesario hacer scroll para revisar el contenido. Al avanzar hacia abajo, llegaba un punto en el que la pantalla se quedaba &#8220;atorada&#8221; y comenzaba a &#8220;saltar&#8221;, como si intentara regresar a una posici\u00f3n anterior.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Despu\u00e9s de realizar un rastreo y de varias pruebas, el comportamiento desaparec\u00eda al quitar el archivo:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;script src=\"\/assets\/js\/iconify-icon.js\"&gt;&lt;\/script&gt;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Esto permiti\u00f3 confirmar que el problema no estaba en Bootstrap, DataTables, CSS personalizado ni en el layout principal, sino en la forma en que Iconify estaba administrando los iconos durante el scroll.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Por qu\u00e9 sucede<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Algunas versiones de <code>iconify-icon.js<\/code> usan <code>IntersectionObserver<\/code> para detectar si un icono est\u00e1 visible dentro del viewport. Cuando el icono sale de pantalla, el script puede remover el SVG renderizado; cuando vuelve a entrar, lo renderiza nuevamente.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">El comportamiento problem\u00e1tico se encuentra en una l\u00f3gica similar a esta:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>    this._observer = new IntersectionObserver((entries) => {\n        const isVisible = entries.some((entry) => entry.isIntersecting);\n\n        if (isVisible !== this._visible) {\n            this._visible = isVisible;\n            this._forceRender();\n        }\n    });\n    <\/code>\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Y dentro de <code>_forceRender()<\/code> puede existir una l\u00f3gica que elimina el icono del Shadow DOM cuando ya no est\u00e1 visible:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>    _forceRender() {\n        if (this._visible) {\n            this._queueCheck();\n        } else {\n            const element = getRenderedIcon(this._shadowRoot);\n\n            if (element) {\n                this._shadowRoot.removeChild(element);\n            }\n        }\n    }\n<\/code>\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">En teor\u00eda, esto busca optimizar el rendimiento. Pero en p\u00e1ginas largas, con muchos iconos y contenido din\u00e1mico, puede causar el efecto contrario: cada vez que el usuario hace scroll, el script modifica el DOM, el navegador recalcula layout, el observer vuelve a detectar cambios y se puede producir un ciclo de renderizado\/recalculo.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">La soluci\u00f3n aplicada<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">La soluci\u00f3n fue desactivar ese comportamiento de observaci\u00f3n de visibilidad y mantener los iconos renderizados de forma estable. Esto evita que Iconify quite y vuelva a insertar SVGs durante el scroll.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Antes de modificar el archivo, es recomendable hacer un respaldo:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cp public\/assets\/js\/iconify-icon.js public\/assets\/js\/iconify-icon.backup.js<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Despu\u00e9s, se pueden aplicar los siguientes ajustes dentro del archivo <code>iconify-icon.js<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">1. Ajustar <code>connectedCallback()<\/code><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Busca una funci\u00f3n similar a esta:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>connectedCallback() {\n    this._connected = true;\n    this.startObserver();\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Y c\u00e1mbiala por:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>connectedCallback() {\n    this._connected = true;\n    this._visible = true;\n    this._queueCheck();\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Con este cambio, al conectar el elemento al DOM, el icono se considera visible y se renderiza sin iniciar el observer.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">2. Corregir la l\u00f3gica de <code>noobserver<\/code><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">En algunas versiones, el manejo del atributo <code>noobserver<\/code> puede estar invertido. Si encuentras algo similar a esto:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>case \"noobserver\":\n    this.hasAttribute(\"noobserver\") ? this.startObserver() : this.stopObserver();\n    break;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">C\u00e1mbialo por:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>case \"noobserver\":\n    this.hasAttribute(\"noobserver\") ? this.stopObserver() : this.startObserver();\n    break;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Esto hace que, si el elemento tiene el atributo <code>noobserver<\/code>, el observer se detenga en lugar de iniciarse.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">3. Reemplazar <code>startObserver()<\/code><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Busca el m\u00e9todo <code>startObserver()<\/code>. Normalmente contiene una instancia de <code>IntersectionObserver<\/code>, similar a esta:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>startObserver() {\n    if (!this._observer &amp;&amp; !this.hasAttribute(\"noobserver\")) {\n        try {\n            this._observer = new IntersectionObserver((entries) =&gt; {\n                const isVisible = entries.some((entry) =&gt; entry.isIntersecting);\n\n                if (isVisible !== this._visible) {\n                    this._visible = isVisible;\n                    this._forceRender();\n                }\n            });\n\n            this._observer.observe(this);\n        } catch (error) {\n            if (this._observer) {\n                try {\n                    this._observer.disconnect();\n                } catch (error) {}\n\n                this._observer = null;\n            }\n        }\n    }\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Reempl\u00e1zalo por una versi\u00f3n simple:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>startObserver() {\n    this._visible = true;\n    this._queueCheck();\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Este es el cambio principal. El icono se mantiene renderizado y ya no se elimina o reconstruye durante el scroll.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">4. Ajustar <code>_forceRender()<\/code><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Busca una funci\u00f3n similar a esta:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>_forceRender() {\n    if (this._visible) {\n        this._queueCheck();\n    } else {\n        const element = getRenderedIcon(this._shadowRoot);\n\n        if (element) {\n            this._shadowRoot.removeChild(element);\n        }\n    }\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Y c\u00e1mbiala por:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>_forceRender() {\n    this._visible = true;\n    this._queueCheck();\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Con esto se evita que cualquier flujo interno vuelva a remover el SVG del Shadow DOM.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">C\u00f3mo evitar problemas de cach\u00e9<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Despu\u00e9s de modificar el archivo, es importante forzar al navegador a cargar la nueva versi\u00f3n. Una forma sencilla es agregar un par\u00e1metro de versi\u00f3n al script:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;script src=\"\/assets\/js\/iconify-icon.js?v=1.0.2\"&gt;&lt;\/script&gt;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Tambi\u00e9n puedes limpiar cach\u00e9 manualmente o compilar nuevamente los assets si el archivo forma parte de un proceso de build.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Alternativa: usar un patch separado<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Si no quieres modificar directamente la librer\u00eda, puedes crear un archivo adicional que se cargue despu\u00e9s de <code>iconify-icon.js<\/code>. Esta opci\u00f3n es \u00fatil cuando el archivo original pertenece a una plantilla o paquete de terceros.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;script src=\"\/assets\/js\/iconify-icon.js\"&gt;&lt;\/script&gt;\n&lt;script src=\"\/assets\/js\/iconify-icon-noobserver-patch.js\"&gt;&lt;\/script&gt;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Sin embargo, si el problema est\u00e1 dentro de la clase registrada como Web Component, modificar directamente el m\u00e9todo dentro del archivo original suele ser m\u00e1s estable, siempre que se haga con respaldo y control de versiones.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Cu\u00e1ndo conviene aplicar esta soluci\u00f3n<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Este ajuste puede ser \u00fatil cuando se cumplen varias de estas condiciones:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>La p\u00e1gina tiene muchos iconos generados con <code>&lt;iconify-icon><\/code>.<\/li>\n\n\n\n<li>El problema aparece \u00fanicamente en p\u00e1ginas largas con scroll.<\/li>\n\n\n\n<li>El salto desaparece al quitar <code>iconify-icon.js<\/code>.<\/li>\n\n\n\n<li>No existen errores claros en consola.<\/li>\n\n\n\n<li>El navegador muestra actividad alta de renderizado o recalculado de estilos.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Recomendaciones finales<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Antes de modificar una librer\u00eda de terceros, conviene confirmar que realmente es la causa del problema. Para hacerlo puedes probar lo siguiente:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Desactivar temporalmente el script sospechoso.<\/li>\n\n\n\n<li>Probar la misma p\u00e1gina con pocos iconos.<\/li>\n\n\n\n<li>Revisar si el problema aparece solo al hacer scroll.<\/li>\n\n\n\n<li>Validar que no existan scripts duplicados.<\/li>\n\n\n\n<li>Hacer respaldo del archivo original antes de editarlo.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">En este caso, la soluci\u00f3n fue mantener los iconos renderizados y desactivar la observaci\u00f3n de visibilidad con <code>IntersectionObserver<\/code>. Con ello se evita que el navegador entre en un ciclo de recalculado al hacer scroll y la interfaz vuelve a comportarse de forma estable.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusi\u00f3n<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Los bugs de scroll pueden ser dif\u00edciles de detectar porque no siempre generan errores en consola. Muchas veces el problema no est\u00e1 en el CSS, sino en scripts que modifican el DOM mientras el usuario navega por la p\u00e1gina.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Cuando una librer\u00eda intenta optimizar el renderizado quitando y agregando elementos durante el scroll, puede terminar provocando saltos visuales, congelamientos o recalculados constantes. En interfaces administrativas con muchos iconos, desactivar ese comportamiento puede ser una soluci\u00f3n pr\u00e1ctica y efectiva.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Si est\u00e1s usando Iconify en su versi\u00f3n 2.1.0 y tienes problemas al hacer scroll en p\u00e1ginas largas este art\u00edculo es para ti&#8230;<\/p>\n","protected":false},"author":1,"featured_media":298,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[11,12,14],"tags":[],"class_list":["post-297","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-plugins","category-html","category-javascript"],"_links":{"self":[{"href":"https:\/\/www.mariatech.com.mx\/blog\/wp-json\/wp\/v2\/posts\/297","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.mariatech.com.mx\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.mariatech.com.mx\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.mariatech.com.mx\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.mariatech.com.mx\/blog\/wp-json\/wp\/v2\/comments?post=297"}],"version-history":[{"count":0,"href":"https:\/\/www.mariatech.com.mx\/blog\/wp-json\/wp\/v2\/posts\/297\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.mariatech.com.mx\/blog\/wp-json\/wp\/v2\/media\/298"}],"wp:attachment":[{"href":"https:\/\/www.mariatech.com.mx\/blog\/wp-json\/wp\/v2\/media?parent=297"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.mariatech.com.mx\/blog\/wp-json\/wp\/v2\/categories?post=297"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.mariatech.com.mx\/blog\/wp-json\/wp\/v2\/tags?post=297"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}