PageSourceSearch

https://sportingclubhuelva.com/js/combo_buscador.js?v=1785412204

js sportingclubhuelva.com collected 2026-10-02 13:36:37 UTC 5,049 bytes, 176 lines download raw bytes

1/**
2 * combo_buscador.js v1.0
3🧩 ¿Qué hace?
4
5Convierte cualquier <select> de un formulario (por clase o selector) en un combo visual personalizado, con buscador, integración Bootstrap 4, compatible con escritorio y móvil. Sustituye a SumoSelect.
6
7📦 FUNCIONALIDADES
8
9✅ Reemplaza visualmente <select> por un combo personalizado.
10✅ Compatible con múltiples selectores y llamadas repetidas.
11✅ Despliega lista con buscador integrado (search: true).
12✅ Cierra cualquier combo abierto al abrir uno nuevo.
13✅ Cierra el combo si haces clic fuera.
14✅ Se enfoca automáticamente el buscador al abrir.
15✅ Compatible con contenido dinámico y AJAX.
16
17*/
18class combo_buscador 
19{
20	constructor(selectorOrElement, opciones = {}) 
21	{
22		let d = {}; // Depuración global
23
24		this.config = Object.assign({
25			placeholder: `Selecciona...`,
26			search: true,
27			multiple: false
28		}, opciones);
29
30		// Array para almacenar elementos a procesar
31		let elementos = [];
32
33		if (typeof selectorOrElement === `string`) 
34		{
35			d.tipo = `string`;
36			elementos = [...document.querySelectorAll(selectorOrElement)];
37			d.selector = selectorOrElement;
38		}
39		else if (selectorOrElement instanceof Element) 
40		{
41			d.tipo = `Element`;
42			d.valorOriginal = selectorOrElement; 
43
44			// Filtrar solo selectores del mismo tipo y misma clase
45			if (selectorOrElement.classList.length) 
46			{
47				const clase = `.` + [...selectorOrElement.classList].join(`.`);
48				elementos = [...document.querySelectorAll(clase)]
49					.filter(el => el.tagName.toLowerCase() === `select`);
50				d.selector = clase;
51			} 
52			else 
53			{
54				elementos = [selectorOrElement];
55				d.selector = `(elemento único sin clase)`;
56			}
57		}
58
59		d.total = elementos.length;
60
61		console.log(`▶ combo_buscador - Constructor`, selectorOrElement, `\nConfiguración:`, this.config);
62		console.table(d);
63
64		elementos.forEach((el, i) => {
65			console.log(`➡ creando combo ${i + 1}/${elementos.length}`, el);
66			this.crear(el);
67		})
68
69		console.log(`✅ combo_buscador finalizado para ${elementos.length} elemento(s).`);
70	}
71
72	crear(selectEl) 
73	{
74		if (!selectEl) return;
75
76		// 🔄 Eliminar wrapper si ya existe
77		if (selectEl.nextElementSibling && selectEl.nextElementSibling.classList.contains(`combo_buscador`)) {
78			console.log(`⚠️ Eliminando combo previo:`, selectEl);
79			selectEl.nextElementSibling.remove();
80		}
81
82		selectEl.hidden = true;
83
84		const wrapper = document.createElement(`div`);
85		wrapper.className = `combo_buscador`;
86
87		const boton = document.createElement(`button`);
88		boton.type = `button`;
89		boton.className = `form-control form-control-sm overflow-hidden`;
90
91		const seleccionada = selectEl.options[selectEl.selectedIndex];
92		boton.textContent = (seleccionada && seleccionada.value.trim() !== ``) 
93			? seleccionada.textContent 
94			: this.config.placeholder;
95
96		wrapper.appendChild(boton);
97
98		const lista = document.createElement(`div`);
99		lista.className = `combo_buscador_opciones`;
100
101		let buscador = null;
102		if (this.config.search) 
103		{
104			buscador = document.createElement(`input`);
105			buscador.type = `text`;
106			buscador.placeholder = `Buscar...`;
107			buscador.className = `bg-light border border-info m-1 p-1`;
108			buscador.style.width = `calc(100% - 0.5rem)`;
109			lista.appendChild(buscador);
110		}
111
112		const ul = document.createElement(`ul`);
113		[...selectEl.options].forEach(op => {
114			const li = document.createElement(`li`);
115			li.textContent = op.textContent;
116			li.dataset.value = op.value;
117			li.addEventListener(`click`, () => {
118				selectEl.value = op.value;
119				boton.textContent = op.textContent;
120				lista.classList.remove(`visible`);
121				const evt = new Event(`change`, { bubbles: true });
122				selectEl.dispatchEvent(evt);
123			});
124			ul.appendChild(li);
125		});
126
127		lista.appendChild(ul);
128		wrapper.appendChild(lista);
129		selectEl.parentNode.insertBefore(wrapper, selectEl.nextSibling);
130
131		boton.addEventListener(`click`, () => 
132		{
133			// 🔹 Cerrar todos los combos abiertos
134			document.querySelectorAll(`.combo_buscador_opciones.visible`).forEach(el => {
135				if (el !== lista) el.classList.remove(`visible`);
136			});
137
138			// 🔹 Abrir/cerrar el actual
139			const visible = lista.classList.toggle(`visible`);
140
141			// 🔹 Si se abre, enfocar el input de búsqueda (en el siguiente frame)
142			if (visible && this.config.search && buscador) 
143			{
144				requestAnimationFrame(() => {
145					buscador.focus();
146				});
147			}
148		});
149
150		if (this.config.search) 
151		{
152			buscador.addEventListener(`input`, () => {
153				const term = buscador.value.toLowerCase();
154				ul.querySelectorAll(`li`).forEach(li => {
155					li.style.display = li.textContent.toLowerCase().includes(term) ? `` : `none`;
156				});
157			});
158		}
159
160		console.log(`✅ combo aplicado a`, selectEl);
161	}
162}
163
164document.addEventListener('click', function(ev) 
165{
166	const combosVisibles = document.querySelectorAll('.combo_buscador_opciones.visible');
167
168	combosVisibles.forEach(lista => 
169	{
170		// Si el clic no fue dentro del combo asociado, cerramos
171		if (!lista.parentNode.contains(ev.target)) 
172		{
173			lista.classList.remove('visible');
174		}
175	});
176});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.