Overview
The ITV application has two main form sections:
Initial Client Form - Captures client information (index.html lines 31-56)
Dynamic Vehicle Forms - Generated per vehicle (script.js lines 58-102)
The initial form collects basic client information:
source/index.html (Lines 34-51)
< div class = "bloque1" >
< div class = "input-group" >
< label for = "nombre" > Nombre </ label >
< input type = "text" id = "nombre" placeholder = "Ingrese su nombre" required >
</ div >
< div class = "input-group" >
< label for = "apellidos" > Apellidos </ label >
< input type = "text" id = "apellidos" placeholder = "Ingrese sus apellidos" required >
</ div >
< div class = "input-group" >
< label for = "razonSocial" > Razón Social </ label >
< input type = "text" id = "razonSocial" placeholder = "Empresa o particular" required >
</ div >
< div class = "input-group" >
< label for = "nAutos" > Nº de Vehículos </ label >
< input type = "text" id = "nAutos" placeholder = "Cantidad de vehículos" required >
</ div >
</ div >
Current Fields
Field ID Label Type Required nombreNombre Text Yes apellidosApellidos Text Yes razonSocialRazón Social Text Yes nAutosNº de Vehículos Text Yes
The form uses a responsive grid layout (.bloque1) that automatically adjusts to 1fr columns on mobile devices.
Adding New Client Fields
Example: Add Email Field
Add HTML Input
Edit source/index.html and add a new input group: < div class = "input-group" >
< label for = "email" > Email </ label >
< input type = "email" id = "email" placeholder = "[email protected] " required >
</ div >
Update Data Collection
Modify obtenerDatosCliente() in script.js (lines 154-161): function obtenerDatosCliente () {
return {
nombre: document . getElementById ( "nombre" ). value . trim (),
apellidos: document . getElementById ( "apellidos" ). value . trim (),
razonSocial: document . getElementById ( "razonSocial" ). value . trim (),
email: document . getElementById ( "email" ). value . trim (), // New
nAutos: numeroAutos
};
}
Update Display Logic
Modify mostrarInformacionCliente() in script.js (lines 210-218): function mostrarInformacionCliente ( datos ) {
document . getElementById ( "informacion" ). innerHTML = `
<h3>👤 Información del Cliente</h3>
<div>Nombre: <span> ${ datos . nombre } </span></div>
<div>Apellidos: <span> ${ datos . apellidos } </span></div>
<div>Email: <span> ${ datos . email } </span></div>
<div>Razón Social: <span> ${ datos . razonSocial } </span></div>
<div>Nº de Vehículos: <span> ${ datos . nAutos } </span></div>
` ;
}
Example: Add Phone Number Field
< div class = "input-group" >
< label for = "telefono" > Teléfono </ label >
< input type = "tel" id = "telefono" placeholder = "+34 XXX XXX XXX"
pattern = "[+0-9\s-]+" required >
</ div >
When adding required fields, ensure you update the validation logic in the enviar() function (lines 8-38) to check for the new field values.
Vehicle forms are generated dynamically by the crearFormularioVehiculo() function:
source/js/script.js (Lines 58-102)
function crearFormularioVehiculo ( numeroVehiculo ) {
return `
<div class="bloque" data-vehiculo=" ${ numeroVehiculo } ">
<h3>🚙 Vehículo Nº ${ numeroVehiculo } </h3>
<p>🔧 Motorización:</p>
<label>
<input type="radio" id="radio1 ${ numeroVehiculo } "
name="motorizacion ${ numeroVehiculo } "
value="Diesel" required>
🛢️ Diesel
</label>
<label>
<input type="radio" id="radio2 ${ numeroVehiculo } "
name="motorizacion ${ numeroVehiculo } "
value="Gasolina">
⛽ Gasolina
</label>
<label>
<input type="radio" id="radio3 ${ numeroVehiculo } "
name="motorizacion ${ numeroVehiculo } "
value="Hibrido">
🔋 Híbrido
</label>
<label>
<input type="radio" id="radio4 ${ numeroVehiculo } "
name="motorizacion ${ numeroVehiculo } "
value="Electrico">
⚡ Eléctrico
</label>
<div class="aprobados">
<p>📋 Estado de la ITV:</p>
<label>
<input type="radio" id="radio5 ${ numeroVehiculo } "
value="Aprobo" name="aprobados ${ numeroVehiculo } ">
✅ Aprobado
</label>
<label>
<input type="radio" id="radio6 ${ numeroVehiculo } "
value="NoAprobo" name="aprobados ${ numeroVehiculo } ">
❌ No Aprobado
</label>
</div>
<label for="observaciones ${ numeroVehiculo } ">📝 Observaciones del vehículo:</label>
<textarea
id="observaciones ${ numeroVehiculo } "
name="observaciones ${ numeroVehiculo } "
class="caja-texto"
placeholder="Ingrese observaciones sobre el estado del vehículo..."
maxlength="250"
rows="4">
</textarea>
</div>
` ;
}
Field Type Purpose Values Radio buttons Motorization type Diesel, Gasolina, Híbrido, Eléctrico Radio buttons ITV Status Aprobo, NoAprobo Textarea Observations Free text (max 250 chars)
Example: Add License Plate Field
function crearFormularioVehiculo ( numeroVehiculo ) {
return `
<div class="bloque" data-vehiculo=" ${ numeroVehiculo } ">
<h3>🚙 Vehículo Nº ${ numeroVehiculo } </h3>
<!-- New field -->
<div class="input-group" style="margin-bottom: 1rem;">
<label for="matricula ${ numeroVehiculo } ">🔖 Matrícula</label>
<input type="text"
id="matricula ${ numeroVehiculo } "
name="matricula ${ numeroVehiculo } "
placeholder="1234ABC"
pattern="[0-9]{4}[A-Z]{3}"
required
style="padding: 0.875rem 1rem; border: 2px solid #e5e7eb;
border-radius: 0.5rem; font-size: 1rem; width: 100%;">
</div>
<p>🔧 Motorización:</p>
<!-- ... rest of form ... -->
</div>
` ;
}
Update Data Processing
After adding new fields, update the procesarDatosVehiculos() function:
source/js/script.js (Lines 189-204 - Modified)
function procesarDatosVehiculos () {
vehiculosData = [];
for ( let i = 1 ; i <= numeroAutos ; i ++ ) {
const motorizacion = document . querySelector ( `input[name="motorizacion ${ i } "]:checked` ). value ;
const estadoITV = document . querySelector ( `input[name="aprobados ${ i } "]:checked` ). value ;
const observaciones = document . getElementById ( `observaciones ${ i } ` ). value . trim ();
// New field
const matricula = document . getElementById ( `matricula ${ i } ` ). value . trim ();
vehiculosData . push ({
numero: i ,
matricula: matricula , // Add to data object
motorizacion: motorizacion ,
estadoITV: estadoITV ,
observaciones: observaciones
});
}
}
Field Validation
The application includes validation logic in the validarFormulariosVehiculos() function:
source/js/script.js (Lines 167-184)
function validarFormulariosVehiculos () {
for ( let i = 1 ; i <= numeroAutos ; i ++ ) {
// Verificar motorización
const motorizacionSeleccionada = document . querySelector (
`input[name="motorizacion ${ i } "]:checked`
);
if ( ! motorizacionSeleccionada ) {
mostrarError ( `Por favor, seleccione la motorización para el vehículo ${ i } .` );
return false ;
}
// Verificar estado ITV
const itvSeleccionado = document . querySelector (
`input[name="aprobados ${ i } "]:checked`
);
if ( ! itvSeleccionado ) {
mostrarError ( `Por favor, seleccione el estado ITV para el vehículo ${ i } .` );
return false ;
}
}
return true ;
}
Add Custom Validation
function validarFormulariosVehiculos () {
for ( let i = 1 ; i <= numeroAutos ; i ++ ) {
// ... existing validations ...
// Validate license plate
const matricula = document . getElementById ( `matricula ${ i } ` );
if ( matricula && ! matricula . value . trim ()) {
mostrarError ( `Por favor, ingrese la matrícula del vehículo ${ i } .` );
return false ;
}
// Validate format (Spanish license plate: 4 digits + 3 letters)
const matriculaPattern = / ^ [ 0-9 ] {4} [ A-Z ] {3} $ / ;
if ( matricula && ! matriculaPattern . test ( matricula . value . trim ())) {
mostrarError ( `Formato incorrecto para matrícula del vehículo ${ i } . Use: 1234ABC` );
return false ;
}
}
return true ;
}
The application includes automatic text capitalization:
source/js/script.js (Lines 509-516)
const camposTexto = [ 'nombre' , 'apellidos' , 'razonSocial' ];
camposTexto . forEach ( campo => {
const input = document . getElementById ( campo );
input . addEventListener ( "input" , function () {
this . value = this . value . charAt ( 0 ). toUpperCase () + this . value . slice ( 1 );
});
});
// Add this to inicializarEventos() function
function formatearMatriculas () {
// This would run after vehicle forms are created
for ( let i = 1 ; i <= numeroAutos ; i ++ ) {
const matriculaInput = document . getElementById ( `matricula ${ i } ` );
if ( matriculaInput ) {
matriculaInput . addEventListener ( "input" , function () {
this . value = this . value . toUpperCase ();
});
}
}
}
// Call after generating forms in enviar()
function enviar () {
// ... existing code ...
generarFormulariosVehiculos ();
// Add formatting
setTimeout (() => {
formatearMatriculas ();
}, 100 );
}
Textarea Configuration
The observations field uses the .caja-texto class:
source/css/styles.css (Lines 270-288)
.caja-texto {
background : #ffffff ;
border : 2 px solid #e5e7eb ;
border-radius : 0.5 rem ;
padding : 1 rem ;
width : 100 % !important ;
font-size : 0.95 rem ;
font-family : inherit ;
transition : all 0.2 s ease ;
resize : vertical ;
min-height : 100 px ;
margin : 0.5 rem 0 !important ;
}
.caja-texto:focus {
outline : none ;
border-color : #2563eb ;
box-shadow : 0 0 0 3 px rgba ( 37 , 99 , 235 , 0.1 );
}
Customize Textarea Size
< textarea
id = "observaciones${numeroVehiculo}"
class = "caja-texto"
placeholder = "Ingrese observaciones..."
maxlength = "500"
rows = "8"
style = "min-height: 150px;" >
</ textarea >
The vehicle count field has special validation:
source/js/script.js (Lines 489-506)
const nAutosInput = document . getElementById ( "nAutos" );
nAutosInput . addEventListener ( "input" , function () {
const valor = parseInt ( this . value );
if ( valor > 50 ) {
this . value = 50 ;
mostrarError ( "El número máximo de vehículos es 50." );
}
if ( valor < 0 ) {
this . value = "" ;
}
});
// Permitir solo números
nAutosInput . addEventListener ( "keypress" , function ( e ) {
if ( ! / [ 0-9 ] / . test ( e . key ) && ! [ 'Backspace' , 'Delete' , 'Tab' , 'Enter' ]. includes ( e . key )) {
e . preventDefault ();
}
});
Field Styling Reference
Input Group Container
Label Styling
Radio Button Styling
.input-group {
display : flex ;
flex-direction : column ;
gap : 0.5 rem ;
}
Best Practices
Use Semantic HTML Use appropriate input types (email, tel, number) for better mobile experience
Add Validation Always validate both client-side and ensure proper error messages
Maintain Consistency Use existing CSS classes (.input-group, .caja-texto) for styling
Test Responsively Test new fields on mobile devices - forms use responsive grids
When adding fields to dynamic vehicle forms, remember to:
Update crearFormularioVehiculo()
Update procesarDatosVehiculos()
Update validarFormulariosVehiculos() if required
Update display functions if showing the data
Quick Reference
File Function Purpose index.htmlN/A Static client form HTML script.js:58-102crearFormularioVehiculo()Generate vehicle form HTML script.js:189-204procesarDatosVehiculos()Collect form data script.js:167-184validarFormulariosVehiculos()Validate inputs script.js:154-161obtenerDatosCliente()Get client data styles.css:109-143.input-group, input[type="text"]Field styling