[Ir al menú de PHP]
Campos de selección dependientes

El problema

De vez en cuando, recibo algunos mensajes de mis visitantes. Algunos me consultan cosas –sospecho que creen que se algo de esto– y casi siempre me obligan a estudiar. Otros tienen la generosidad de tratar de levantarme la autoestima y algunos otros –pocos, la gente es generosa– pues me llaman de todo menos guapo. ;-)

Hace unos días, Javier me envió un mensaje en el que me preguntaba si sabía como construir un formulario con varios select de manera que el contenido de los sucesivos dependiera del valor elegido en los anteriores.

Me pertreché del donsabido acetilsalicílico y me puse hacer mis experimentos (con sifón, como siempre).

Me acordé que hace un tiempo alguien me puso de sopa dómine por el grave delito de escribir en la primera página de estas Memorias la ofensiva frase: ...con el «Nescafé» (leáse Netscape)...

Al parecer a mi dilecto comunicante –defensor de Linux a ultranza y alérgico a cuanto sea MicroSoft– tal frase le pareció un insulto a quienes apoyan el uso de software libre.

Aunque lo mío es pura ignorancia -soy sólo un pobre aprendiz sin demasiado criterio propio- ese mensaje empezó a preocuparme y mucho... Sólo me falta que la multinacional de Vevey me plante una querella por menosprecio de su marca y acabe con mis artrósicos huesos en Villabona.

Por eso me propuse –bendita demencia– tratar de resolver el problema de modo que andara al menos en: Firefox, Mozilla y Explorer.

El evento onChange

Como de JavaScript se lo mismo que de búlgaro, tuve que empezar a especular sobre la posibilidad de hacer algo con el valor elegido en la opción. Descubrí -puro ensayo y error y búsquedas desesperadas en la gües- que si incluía onChange dentro de una etiqueta select podía llamar a una función Jovascript cuando cambiara el valor del select.

Descubrí también que incluyendo en el paréntesis this.value (sin comillas ni nada) se incluye el valor seleccionado en la llamada a la función.

Modificar una IFRAME

El problema estaba en ver la forma de cambiar el contenido de la IFRAME que debería contener la lista de Comunidades autónomas. Por el momento contenía la famosa página en blanco pero ahí había que incluir un nuevo select.

Tenía que decir que se cargara la página tal en la IFRAME cual. Para enterarme de como indicar la IFRAME que quería tardé... ufffff... mejor ni te lo digo. Cuando pensaba que yatá la catástrofe... no funcionaba más que en Explorer, en fín... todo un poema. Descubrí que poniendo:

window.parent.frames['nom'] el navegador entendía que le hablaba de la ventana principal y de la IFRAME cuyo name escribía (entre comillas) dentro del corchete.

Ya tenía la ventana tal. Sólo me faltaba decirle abre la página cual. Eso fué más fácil. Poniendo detrás de la ventana .location='pag' me abría la página indicada en la ventana indicada. ¡Parecía marchar!. Pero...

Modificar valores de campos de un formulario

Tal como te comento a la derecha, tuve que incluir un formulario en el documento principal para recoger (todos juntos y en unión) los valores de las diferentes opciones de selección.

Como el formulario estaba en la página principal, para localizarlo tuve que escribir el medio metro de texto que ves en el recuadro de modificación de la función. Creo yo que la razón (yo lo hice por el método PHS -Probar Hasta que Funcione-) es que: window.parent es la ventana principal, en ella hay que buscar el document, dentro del documento los formularios (forms), entre los formularios el que tenga el name correspondiente y -ya en el formulario- hay que indicar el nombre del campo y por fin... hay que decir que queremos hacer allí. Por eso el .value=valor que es como decirle: escribe allí este valor, pero en fino..

Ah.. se me olvidaba... cuidadito con los puntos. Olvidar uno es... un dolor de cabeza.. te lo juro ;-)

La opción radio

En este caso, incluí en la capa correspondiente las etiquetas <input» de cada estado civil posible (olvidé incluir las últimas reformas legislativas) pero estaba en las mismas. Ahora quería que al elegir una opción cambiara la imagen y además los valores elegidos tendría que incluirlos en los value del macroformulario del que te hablo más arriba.

De nuevo la pelea con JavaScript. Aquí tuve que incluir onClick en cada uno de los input. De esta forma, cada vez que pulso llamo a la función estado y le paso this.value (esto igual que en los casos anteriores).

La funcion estado es muy similar a las anteriores. Más sencilla. Ahora no tiene que borrar campos select y su labor consiste en cambiar el valor del formulario final y pasar la opción a la página de gestión.

En este caso, el case correspondiente solo tiene que insertar en el IFRAME una imagen asociada al botón pulsado. La imagen del soltero o la del casado, etc.

Las casillas de verificación

Después de muchos fracasos encontré esta solución ¿un poco chapucilla, verdad?.

Me di cuenta de que this.checked tiene valor true si la casilla está marcada y false cuando no lo está. También observé que onClick y this.value funcionan igual que el caso anterior. Pero además de eso, añadí un tercer valor a la llamada a la función idioma. Le incluí un número de orden (del cero al tres) para poder identificar en que casilla había pulsado.

Opté por crear una array -flags_idioma- con un indice para cada casilla y cuatro valores, inicialmente *, que equivalen a que no está seleccionada esa opción. Al marcar o desmarcar una casilla la función recibe: true o false; el número de casilla y el valor.

Si recibe true (casilla marcada) modifica el array y le inserta el valor al elemento cuyo índice coincide con la casilla activada. Cuando recibe false poner un asterisco.

El resto del proceso es idéntico a los anteriores. Pone el contenido del array como value del campo asignado en el formulario oculto y transfiere el contenido de ese array al script que inserta la banderitas en el iframe correspondiente.

El select MULTIPLE

Antes de que me olvide. En las opciones anteriores no necesité meter ni checkbox, ni radio dentro de etiquetas form. Aquí tuve que hacerlo. Fué la única manera de lograr que funcionara. ¡Ojo!.

Otra diferencia respecto a los casos anteriores está en el evento onChange. Fíjate que en este caso no hace falta poner this.value tal como hacíamos antes. La razón es la inclusión de la etiqueta form.

Respecto a la función la cosa es como sigue. Creamos una variable (campos) que va a contener la dirección del select: window.parent (indica la ventana principal), el document (no es una frame, es la propia página), forms (se trata de un formulario), gustos (es el nombre del formulario) y gustosS (es el nombre del select múltiple).

JavaScript puede leer en ese select en número de options que contiene (length) y puede recorrerlos (bucle for) comprobando si están o no seleccionados (selected). Si un campo está seleccionado (true) lo incluye (seguido de una coma) en la cadena (seleccionados) y si no lo está (false) incluye (*,). Hecho esto, ya es cuestión de pasar el valor al formulario, y mandar que script gordo ejecute el switch correspondiente que en este caso incluirá la imagen en el iframe -cuando hay valor distinto del *- y no lo hará en caso contrario.

onMouseOut

El truco que encontré para actualizar el formulario oculto al modificar el textarea fue este evento. Se ejecuta cada vez que el ratón sale de él. De esa forma, transfiere al formulario el contenido de este area cada vez que dejamos de escribir y nos vamos a otro.

El botón de enviar

No presenta mayores complicaciones. Es un input type button (también podría ser una imagen que lleva asociado el evento onClick que lo que hace es submit() (enviar) el formulario que se indica en toda la ristra que precede al submit.


 
 


El resultado final del experimento

    En este enlace tienes resultado final del experimento que he estado haciendo para tratar de resolver el problema de un formularico con campos de selección interdependientes.
Select dependiente

Los primeros pasos

Empecé a pensar que el alma del asunto estaba en lograr modificar solo una parte de la página al ir eligiendo en las diferentes opciones de selección. Buscando... (lo cierto es que jamás los había usado) me encontré con etiqueta HTML IFRAME y me puse a enterarme un poco del asunto. Vi que la sintaxis era de este estilo:

<IFRAME name="" src="" height="" width="" scrolling="" frameborder="" marginheight=" " marginwidth="" AllowTransparency>

y me enteré también que en name podía ponerse un nombre a la frame famosa, en src la dirección de la página que pretendemos incluir en la IFRAME, que en height y en height podía ponerse la altura y el ancho (en pixels o en porcentaje) que en scrolling podía ponerse yes, no ó auto (permitir, no permitir o permitir si hace falta la barrita de scroll), que frameborder podía poner el ancho del borde (incluso podía ser cero) y que marginheight y en marginwidth era donde se asignaban los márgenes de la página dentro de la frame y... que incluyendo AllowTransparency (al menos eso había leido en la gües) podría verse el fondo de la página en la que estaba incrustada la IFRAME.

Esto último resultó ser verdad a medias. Luego te cuento... ;-)

Todo esto estaba muy bien pero, tenía un problema. ¿Como podía posicionar la IFRAME dentro de la página?. Podía asignarle un ancho y una altura pero... ¿para colocarla?. Encontre la solución metiéndola dentro de una capa (una <DIV>, para entendernos) y que todo era cuestión de ubicar esa capa mediante una hoja de estilo.

Me propuse –burro grande, ande o no ande– intentar elaborar un formulario más o menos completo. Así que empecé por una maqueta... más o menos como esta.

Maqueta previa

Me puse a incluir las IFRAME en cada una de las DIV, poniendo en todas ellas el ancho y el alto como 100% (había puesto ambas dimensiones a las DIV y pensaba que se ajustarían a esas dimensiones, pero... en Firefox y en Mozilla la cosa funcionaba, pero... en Explorer solo ajustaba el ancho pero el alto... naranjas de la china. Lo resolví poniendo como height del IFRAME el mismo que había puesto a cada una de las capas.

Tambien puse scrolling="no", frameborder="1" y el famoso AllowTransparency y creé una página en blanco con fondo rojo (blanco.php) que fué la que incluí como src en todas las IFRAME y me puse a probar.

Jejeje... (por algo decía yo lo del Nescafé). Resultó que todo iba bien, pero... en Explorer de transparente nada de nada. Después de un montón de vueltas resolví el problema de compatibilidad nodificando la etiqueta BODY de la página en blanco y poniendo: <BODY BGCOLOR="#FF0000" style="background-color: transparent;"> y ahora si que andó en los tres navegadores.


No transparente en IE Transparente

El primer select

Tengo una tabla de países –no se si completa del todo– con un único campo. Así que decidí utilizarla para incluir una lista de paises en el primer select. Cambié el src del primer select y lo sustituí por una página en la que leía la lista de paises completa y la incluía en dentro del select de un formulario.


<
....
... el rollete de conexion al servidor MySQL, etc. etc.
$tabla="paises";
$resultado= mysql_query("SELECT* FROM $tabla ORDER BY Nombres",$conexion);
?>
<form name="a1">
<select name="paises" onchange="cambia1(this.value);">
<option value=""> 
<?
while ($registro = mysql_fetch_row($resultado)){
  print "<option value='".$registro[0]."'>".$registro[0]."</option>\n";
}
print "</select>";
print "</form>";
mysql_close();
?>
<script>
function cambia1(valor){
  var pagina="selector.php?op=comunidad&valor="+valor;
  window.parent.frames["iframe_comunidad"].location=pagina;
  window.parent.frames["iframe_provincia"].location="blanco_trans.php";
  window.parent.frames["iframe_municipio"].location="blanco_trans.php";
  window.parent.frames["iframe_localidad"].location="blanco_trans.php";
}
<script>

Fíjate que el primer location. Le he puesto igual a la variable página. Y en esa variable incluí: selector.php? (que es el nombre de la página que contiene los script), op=comunidad (para pasarle una variable indicando que quiero cargar comunidades) y la variable valor que recoge el valor seleccionado para pasarlo a selector.php.

Lo hice así para no tener que usar páginas distintas para cada IFRAME. Incluyendo un switch con sus case asociados al valor op no necesitaba una página para cada select.

También incluí en la función JavaScript la opción de recargar la página en blanco en todas las IFRAMES siguientes. Lo hice así para evitar que Asturias apareciera como una comunidad de Botswana... que fué lo que me pasó cuando hacía las pruebas.

Había seleccionado España y Asturias (no te preocupes... no te colocaré la babayada esa de que «...Asturias es España y lo demás tierra reconquistada..») y al cambiar de país seguía Asturias allí plantada. La solución fué la que ves: al cambiar un select hay que poner el blanco todos los que van detrás. De esa forma Asturias estará en España (al menos por el momento... que vaya usted a saber).

Todo muy bonito pero...

Algo estaba fallando. Se entendía que los valores elegidos en cada una de las opciones –Iframe p'arriba Iframe p'abajo– habría que enviarlos juntos a alguna parte y claro... en el trasiego se perdían. La solución fué incluir un formulario –en la página principal– con campos ocultos para que no se vean... ;-) que en principio tuviera todos los values en blanco.


<form name="resultados_finales" method="post" 
                   action="selector.php?op=resultados">
<input type="hidden" name="f_pais" value="">
<input type="hidden" name="f_comunidad" value="">
<input type="hidden" name="f_provincia" value="">
<input type="hidden" name="f_municipio" value="">
<input type="hidden" name="f_localidad" value="">
<input type="hidden" name="f_estado" value="">
<input type="hidden" name="f_idioma" value="">
<input type="hidden" name="f_gustos" value="">
<input type="hidden" name="f_historia" value="">
</form>

Había que insertar los values... Esa batalla te la cuento al margen pero tuve que añadir una línea nueva a la función JavaScript.


function cambia1(valor){
.......
window.parent.document.forms.resultados_finales.f_pais.value=valor;
.....
}

De oca en oca...

Superado el trance amargo de hacer el primer select los demás fueron relativamente fáciles. Al hacer el onChange en paises cargaba en la iframe siguiente la misma página, pero como le pasaba op=comunidades buscaba el case correspondiente en el switch y ahora consultaba una nueva tabla –la de comunidades– y cargaba su contenido en el nuevo select.

Claro, puse el script de modo que si el pais seleccionado no era España –si tienes la lista de comunidades autónomas de Botswana, pásamela ;-)– aparece el mensajito diciendo: No existen datos para de este país.

Desde la lista de comunidades –es pura repetición de lo anterior– cargaba el nuevo select con las provincias, desde este el de los municipios y desde este último las localidades. Claro que cada nuevo select necesita una función distinta para onChange. Recuerda que desde esa función se ponen en blanco los select posteriores, se actualiza el valor de un campo del formulario distinto y además, se carga la página con una op distinta.

La opción radio


<div id="opciones_estado">
<input type="radio" name="estado" value="soltero" 
                       onClick="estado(this.value);">Solter@<br>
<input type="radio" name="estado" value="casado"  
                       onClick="estado(this.value);">Casad@<br>
<input type="radio" name="estado" value="divorciado"  
                       onClick="estado(this.value);">Divorciado@<br>
</div>
<script>
function estado(valor){
  pagina="selector.php?op=estado&v="+valor;
  window.frames["iframe_estado"].location =pagina;
  document.forms.resultados_finales.f_estado.value=valor;
}
</script>

La casilla de verificación


<div id="opciones_idioma">
<input type="checkbox" value="castellano" 
          onclick="idioma(this.checked,0,this.value);">Español<BR>
<input type="checkbox" value="bulgaro" 
          onclick="idioma(this.checked,1,this.value);">Bulgaro<BR>
<input type="checkbox" value="armenio" 
          onclick="idioma(this.checked,2,this.value);">Armenio<BR>
<input type="checkbox" value="chino" 
          onclick="idioma(this.checked,3,this.value);">Chino
</div>
<script>
var flags_idioma= new Array();
flags_idioma[0]="*";
flags_idioma[1]="*";
flags_idioma[2]="*";
flags_idioma[3]="*";
function idioma(estado, nombre, valor){
	if (estado==true){
	flags_idioma[nombre]=valor;
	}else{
	flags_idioma[nombre]='*';
	}
  document.forms.resultados_finales.f_idioma.value=flags_idioma;
  pagina="selector.php?op=idioma&v="+flags_idioma;
  window.frames["iframe_idiomas"].location =pagina;

}
</script>

Ojo al parche....

Tanto al asignar el valor al campo hidden del formulario como al pasar la variable al script selector.php el array se transfiere como una cadena única con los elementos separados con comas.

Habrá que tenerlo en cuenta y reconvertirlo de nuevo array al ser tratado por PHP. Ya sabes:

     $nuevo_array=explode(',',cadena_transferida)

El select múltiple


<form name="gusto">
<SELECT MULTIPLE name="gustosS" SIZE=4 onChange="gustos();">
<option value="chorizo">Bocatas de jamón de Tineo</option>
<option value="vino">Tintorro «Marqués del Pelleyu»</option>
<option value="whisky">Whisky «Dragados Y Construcciones»</option>
<option value="queso">Queso de Cabrales</option>
</select>
</form>

<script>
function gustos(valor){
	var campos=window.document.forms.gusto.gustosS; 
	var seleccionados = "";
      for(var i = 0;i < campos.length;i++){
            if(campos.options[i].selected == true){
                   seleccionados += campos.options[i].value+",";
               }else{
                   seleccionados += "*,";
             }
	   
       }
  document.forms.resultados_finales.f_gustos.value=seleccionados;
  pagina="selector.php?op=gustos&v="+seleccionados;
  window.frames["iframe_gustos"].location =pagina;

}
</script>

Cuidado....

La cadena transferida en este caso lleva una coma al final

Habrá que tenerlo en cuenta y quitarla desde PHP -con un subsrt- antes de reconvertirla en un array.

Área de texto


<textarea name="historia" rows=8 cols=45 
                 onMouseOut="historia(this.value);">
</textarea>

<script>
function historia(valor){
document.forms.resultados_finales.f_historia.value=valor;
</script>

El botón de Enviar


<div id="enviar">
<input type="button" value="Enviar"
     onclick="document.forms.resultados_finales.submit();">
</div>

Y... colorín colorado... ya te conté cuanto aprendí a respecto de este asunto. Gracias Javier, me has hecho sudar pero algo conseguí aprender. ;-)


Sugerir a un/a amig@ Envíame tus comentarios
Anterior
Indice
Siguiente