---
id: KB-PLUGIN-015
title: querySelector('.row') colide com elementos de outros plugins no Helpdesk portal
domain: plugin-dev
tags:
- glpi
- glpi11
- helpdesk
- tilesections
- dom
- selectors
- bootstrap
- cross-plugin-interference
status: active
severity: high
created_at: 2026-05-10
updated_at: 2026-05-10
applies_to:
- GLPI 11.0.x
---
# `querySelector('.row')` colide com outros plugins no Helpdesk portal
## Sintoma
Plugin funciona perfeitamente no ambiente de desenvolvimento mas falha **silenciosamente** em produção — não há erro no console, não há AJAX falhando, mas o DOM não é manipulado. A causa é difícil de descobrir porque o servidor retorna os dados corretos e o JS chega a rodar.
No caso real: `tilesections/public/js/helpdesk_home.js` em produção não reorganizava os tiles em categorias. Em dev funcionava. A diferença era a presença de **outro plugin** (`news_alert`) em produção que injetava um elemento com classe `row` antes dos tiles.
## Causa
A `container-xl` do portal do Helpdesk pode conter múltiplos elementos com class `row`:
```html
```
Código defeituoso:
```js
const container = tilesSection.querySelector('.container-xl');
const tilesRow = container.querySelector('.row'); // ❌ pega o do news_alert
```
`querySelector` faz busca em profundidade e retorna o **primeiro** match na árvore — incluindo elementos aninhados. O `plugin_news_alert-container` está dentro da `` que vem **antes** da row dos tiles, então é encontrado primeiro.
Depois disso, `tilesRow.querySelectorAll(':scope > div')` retorna zero (o container do news plugin está vazio), e a função desiste sem fazer nada.
## Padrão correto
Usar `:scope > .row` para garantir que apenas **filhos diretos** sejam considerados:
```js
const tilesRow = container.querySelector(':scope > .row'); // ✅ só filhos diretos
```
`:scope` referencia o elemento de partida da query (no caso, `container`). `:scope > .row` significa "row que é filha direta de container".
## Lições
- **Nunca confie que sua produção tem a mesma estrutura DOM do dev**. Plugins de terceiros (news, dashboards, customizações antigas) injetam elementos em pontos imprevisíveis.
- Para qualquer seletor que dependa de hierarquia, **prefira `:scope >` ou caminhos específicos** ao invés de buscas globais com classes Bootstrap genéricas como `.row`, `.card`, `.col-*`.
- Quando funcionar local e não em produção, **não assuma diferenças de container/Docker**. Inspecione o DOM real do cliente. O usuário pode rodar no console:
```js
document.querySelector('.tiles-banner .container-xl').innerHTML.substring(0, 600)
```
e mandar o resultado.
## Hierarquia de especificidade recomendada
Para seletores de containers em plugins GLPI, em ordem de robustez:
1. **`[data-glpi-*]` atributos** — quando GLPI fornece (ex: `[data-glpi-helpdesk-config-tiles]`). Mais estável.
2. **`:scope > .classe`** — quando só filhos diretos importam.
3. **Caminho específico** — ex: `.tiles-banner > .container-xl > .row`.
4. **`.row` solto** — **evitar**. Praticamente garante colisão com outros plugins.
## Onde isso pegou na vida real
`/home/glpi/glpi_dev/plugins/tilesections/public/js/helpdesk_home.js::reorganizeTiles()` — corrigido em `tilesections v1.1.2`. O bug não era reproduzível em dev porque o ambiente Docker não tinha o plugin `news_alert` instalado.