Semuanya mengalir, semuanya berubah, tetapi hanya
input[type=file]
karena merusak saraf semua pengembang web pemula, dan terus melakukannya hingga sekarang. Ingat diri Anda N tahun yang lalu, ketika Anda baru saja mulai memahami dasar-dasar membuat situs web. Muda dan tidak berpengalaman, Anda benar-benar terkejut ketika tombol pemilihan file benar-benar menolak untuk mengubah warna latar belakang ke persik favorit Anda. Pada saat itulah Anda pertama kali menemukan gunung es yang tidak bisa dihancurkan ini yang disebut “Mengunduh File,” yang hingga hari ini terus “menenggelamkan” pengembang web pemula.
Menggunakan contoh membuat bidang unggahan file, saya akan menunjukkan kepada Anda cara menyembunyikan
input[type=file]
dengan benar, mengatur fokus pada objek yang tidak dapat memiliki fokus, menangani acara Seret-dan-Letakkan dan mengirim file melalui AJAX. Dan saya juga akan memperkenalkan Anda dengan beberapa bug browser dan cara untuk mengatasinya. Artikel ini ditulis untuk pemula, tetapi di beberapa titik dapat bermanfaat dan menghibur bahkan untuk pengembang berpengalaman.
Markup dan gaya primer
Mari kita mulai dengan markup HTML:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title> , </title> <link rel="stylesheet" href="style.css"> <script type="text/javascript" src="jquery-3.3.1.min.js"></script> <script type="text/javascript" src="script.js"></script> </head> <body> <form id="upload-container" method="POST" action="send.php"> <img id="upload-image" src="upload.svg"> <div> <input id="file-input" type="file" name="file" multiple> <label for="file-input"> </label> <span> </span> </div> </form> </body> </html>
Mungkin elemen utama yang harus Anda perhatikan adalah
<label for="file-input"> </label>
Spesifikasi HTML tidak memungkinkan kami untuk memaksakan properti visual secara langsung pada
input[type=file]
, tetapi kami memiliki tag
label
, mengklik yang menyebabkan klik pada elemen formulir yang dilampirkan. Yang menggembirakan kami, tag ini tidak memiliki batasan dalam stilisasi: kami dapat melakukan apa pun yang kami inginkan dengannya.
Sebuah rencana aksi muncul: kami menyesuaikan label sesuka kami, dan menyembunyikan
input[type=file]
dari pandangan. Pertama, atur gaya halaman umum:
body { padding: 0; margin: 0; display: flex; justify-content: center; align-items: center; min-height: 100vh; } #upload-container { display: flex; justify-content: center; align-items: center; flex-direction: column; width: 400px; height: 400px; outline: 2px dashed #5d5d5d; outline-offset: -12px; background-color: #e0f2f7; font-family: 'Segoe UI'; color: #1f3c44; } #upload-container img { width: 40%; margin-bottom: 20px; user-select: none; }
Sekarang gaya label kami:
#upload-container label { font-weight: bold; } #upload-container label:hover { cursor: pointer; text-decoration: underline; }
Apa yang kami perjuangkan (
input[type=file]
dihapus dari markup):

Tentu saja, Anda dapat memusatkan label, menambahkan latar belakang dan perbatasan, mendapatkan tombol penuh, tetapi prioritas kami adalah Drag-and-Drop.
Menyembunyikan input
Sekarang kita perlu menyembunyikan
input[type=file]
. Hal pertama yang mengejutkan kepala adalah
display: none
dan
visibility: hidden
properti
visibility: hidden
. Tapi ini tidak sesederhana itu. Pada beberapa browser lama, mengklik label tidak akan berpengaruh lagi. Tapi itu belum semuanya. Seperti yang Anda ketahui, elemen tak terlihat tidak dapat menerima fokus, dan apa pun yang mereka katakan, fokus itu penting, karena bagi sebagian orang ini adalah satu-satunya cara untuk berinteraksi dengan situs. Jadi metode ini tidak cocok untuk kita. Mari kita berkeliling:
#upload-container div { position: relative; z-index: 10; } #upload-container input[type=file] { width: 0.1px; height: 0.1px; opacity: 0; position: absolute; z-index: -10; }
Kami benar-benar memposisikan
input[type=file]
kami
input[type=file]
relatif terhadap blok induknya, menguranginya menjadi
0.1px
, membuatnya transparan dan mengatur
z-index
lebih kecil dari pada induknya, sehingga, tentu saja, tentu saja.
Selamat, kami mencapai apa yang kami inginkan: bidang kami terlihat persis seperti pada gambar sebelumnya.
Sesuaikan fokus
Karena
input[type=file]
kami
input[type=file]
fisik ada di halaman, ia memiliki kemampuan untuk menerima fokus. Artinya, jika kita menekan tombol
Tab
pada halaman, maka pada titik tertentu fokus akan beralih ke
input[type=file]
. Tetapi masalahnya adalah kita tidak akan melihat ini: bidang yang kita sembunyikan akan menonjol. Ya, jika saat ini kita menekan
Enter
, kotak dialog akan terbuka dan semuanya akan berfungsi sebagaimana mestinya, tetapi bagaimana kita memahami bahwa ini saatnya untuk menekan?
Tugas kami adalah memilih tanda dengan cara tertentu pada saat fokusnya adalah pada bidang unggah file. Tetapi bagaimana kita dapat melakukan ini jika tag tidak dapat menerima fokus? Penikmat CSS3 akan segera berpikir tentang pseudo-class
:focus
, yang mendefinisikan gaya untuk elemen dalam fokus, dan
+
atau
~
penyeleksi yang memilih tetangga yang tepat: elemen yang terletak di tingkat bersarang yang sama, datang setelah elemen yang dipilih. Menimbang bahwa dalam
input[type=file]
markup kami
input[type=file]
terletak tepat di depan tag
label
, entri berikut terjadi:
#upload-container input[type=file]:focus + label { }
Tapi sekali lagi, tidak sesederhana itu. Untuk memulai, mari kita bahas bagaimana kita harus memilih label. Seperti yang Anda ketahui, semua browser modern dan bukan jadi memiliki properti default unik untuk elemen-elemen dalam fokus. Pada dasarnya, ini adalah properti
outline
, yang menciptakan goresan di sekitar elemen yang berbeda dari
border
karena tidak mengubah ukuran elemen dan dapat dipindahkan dari situ. Sebagai aturan, orang hanya menggunakan satu browser, sehingga mereka terbiasa dengan standarnya. Untuk mempermudah orang menavigasi situs kami, kami harus mencoba menyesuaikan fokus sehingga terlihat sealami mungkin untuk sebagian besar browser modern yang populer. Secara teori, menggunakan JavaScript, Anda bisa mendapatkan informasi tentang browser tempat pengguna membuka situs tersebut, dan menyesuaikan gaya yang sesuai, tetapi topik ini terlalu rumit dan rumit sebagai bagian dari artikel yang ditujukan terutama untuk pemula. Kami akan mencoba bertahan dengan sedikit darah.
Di browser yang didasarkan pada mesin WebKet (Google Chrome, Opera, Safari), properti default untuk elemen dalam fokus adalah dalam bentuk:
:focus { outline: -webkit-focus-ring-color auto 5px; }
Di sini
-webkit-focus-ring-color
adalah
-webkit-focus-ring-color
fokus-stroke khusus untuk mesin ini saja. Yaitu, baris ini akan bekerja secara eksklusif di browser WebKit, dan inilah yang kami butuhkan. Tentukan properti ini untuk label kami:
#upload-container input[type=file]:focus + label { outline: -webkit-focus-ring-color auto 5px; }
Kami membuka Google Chrome atau Opera, kami melihat. Semuanya berfungsi sebagaimana mestinya:

Mari kita lihat bagaimana semuanya dengan fokus di Mozilla Firefox dan Microsoft Edge. Untuk browser ini, properti default adalah:
:focus { outline: 1px solid #0078d7; }
dan
:focus { outline: 1px solid #212121; }
sesuai.
Sayangnya, awalan
-moz-
tidak akan berfungsi dengan properti
outline
. Karena itu, kita harus memilih mana dari dua properti yang kita pilih. Karena jumlah pengguna Firefox jauh lebih tinggi, itu lebih rasional untuk memberikan preferensi ke browser khusus ini. Ini tidak berarti bahwa kami menghilangkan kesempatan bagi pengguna Edge dan browser lain untuk melihat di mana fokusnya sekarang, itu hanya akan terlihat “berbeda” dari mereka. Nah, Anda harus berkorban.
Kami menambahkan gaya dari Mozilla Firefox sebelum gaya untuk WebKit: pertama semua browser akan menerapkan properti pertama, dan yang dapat (Google Chrome, Opera, Safari, dll.) Akan menerapkan yang kedua.
#upload-container input[type=file]:focus + label { outline: 1px solid #0078d7; outline: -webkit-focus-ring-color auto 5px; }
Dan di sini mulai aneh: di Edge semuanya berfungsi dengan baik, tetapi Firefox untuk beberapa alasan yang tidak diketahui menolak untuk menerapkan properti ke label dengan fokus pada
input[type=file]
. Dan acara
focus
itu sendiri terjadi - diperiksa melalui JavaScript. Selain itu, jika Anda memaksakan fokus pada bidang pemilihan file melalui alat pengembang, maka properti akan berlaku dan langkah kami akan muncul! Rupanya, ini adalah bug dari browser itu sendiri, tetapi jika seseorang memiliki ide mengapa ini terjadi - tulis di komentar.
Yah, tidak ada, pahlawan normal selalu berkeliling. Seperti yang saya katakan sebelumnya, acara
focus
terjadi, yang berarti bahwa kita dapat menyesuaikan properti langsung dari JavaScript. Tetapi untuk ini kita harus mengubah logika pemilih kita:
#upload-container label.focus { outline: 1px solid #0078d7; outline: -webkit-focus-ring-color auto 5px; }
Kami akan menjelaskan kelas
.focus
untuk label kami dan kami akan menambahkannya setiap kali ketika
input[type=file]
menerima fokus dan menghapusnya ketika kehilangan.
$('#file-input').focus(function() { $('label').addClass('focus'); }) .focusout(function() { $('label').removeClass('focus'); });
Sekarang semuanya berjalan sebagaimana mestinya. Selamat, kami telah menemukan fokus.
Seret dan lepas
Bekerja dengan Drag-and-Drop dilakukan dengan melacak peristiwa browser khusus:
drag, dragstart, dragend, dragover, dragenter, dragleave, drop
. Anda dapat dengan mudah menemukan deskripsi terperinci masing-masing di Internet. Kami hanya akan melacak beberapa dari mereka.
Pertama, tentukan elemen seret dan lepas:
var dropZone = $('#upload-container');
Kemudian kami menjelaskan dalam CSS sebuah kelas khusus yang akan kami tetapkan
dropZone
ketika kursor menarik file tepat di atasnya. Ini diperlukan untuk memberi tahu pengguna secara visual bahwa file tersebut sudah dapat dirilis.
#upload-container.dragover { background-color: #fafafa; outline-offset: -17px; }
Sekarang mari kita pindah ke file JS. Untuk memulai, kita perlu membatalkan semua tindakan default untuk acara Drag-and-Drop. Sebagai contoh, salah satu dari peristiwa tersebut adalah pembukaan file yang dilemparkan oleh browser. Kami tidak membutuhkan ini sama sekali, jadi kami akan menulis baris berikut:
dropZone.on('drag dragstart dragend dragover dragenter dragleave drop', function(){ return false; });
Di jQuery, memanggil
return false
sama dengan memanggil dua fungsi sekaligus:
e.preventDefault()
dan
e.stopPropagation()
.
Kami mulai menggambarkan event handler kami sendiri. Kami akan bertindak dengan cara yang sama seperti yang kami lakukan dengan fokus, tetapi kali ini kami akan melacak peristiwa
dragenter
dan
dragover
untuk menambahkan kelas dan acara
dragleave
untuk menghapusnya:
dropZone.on('dragover dragenter', function() { dropZone.addClass('dragover'); }); dropZone.on('dragleave', function(e) { dropZone.removeClass('dragover'); });
Dan lagi, kejutan yang tidak menyenangkan menunggu kami: ketika Anda bergerak bersama
dropZone
dengan mouse dengan file, bidang mulai berkedip. Ini terjadi di browser Microsoft Edge dan WebKit. Ngomong-ngomong, sebagian besar browser WebKit ini sedang berjalan di mesin Blink (menghargai ironi, ya?). Tapi di Mozilla, tidak ada yang berkedip. Rupanya, saya memutuskan untuk memperbaikinya setelah fokus bug.
Dan kerlipan ini terjadi karena fakta bahwa ketika Anda mengarahkan kursor ke
dropZone
, baik itu gambar atau
div
dengan bidang pemilihan file dan label, untuk beberapa alasan acara
dragleave
. Jelas bagi kami bahwa kami tidak meninggalkan lapangan, tetapi browser, untuk beberapa alasan, tidak, dan karena ini mereka
.focus
-
.focus
menghapus kelas
dropZone
dari
dropZone
.
Dan lagi, kita harus keluar entah bagaimana. Jika peramban itu sendiri tidak mengerti bahwa kami tidak meninggalkan lapangan, kami harus membantunya. Dan kami akan melakukan ini melalui kondisi tambahan: kami menghitung koordinat tetikus relatif ke
dropZone
, dan kemudian memeriksa apakah kursor berada di luar blok. Jika dibiarkan, maka kami menghapus gaya:
dropZone.on('dragleave', function(e) { let dx = e.pageX - dropZone.offset().left; let dy = e.pageY - dropZone.offset().top; if ((dx < 0) || (dx > dropZone.width()) || (dy < 0) || (dy > dropZone.height())) { dropZone.removeClass('dragover'); }; });
Dan semuanya, masalahnya selesai! Seperti inilah bidang kami dengan file di dalamnya:

Mari kita beralih ke menangani event
drop
itu sendiri. Tetapi pertama-tama, ingatlah bahwa, selain Drag-and-Drop, kami memiliki
input[type=file]
, dan masing-masing metode ini independen dalam esensinya, tetapi harus melakukan tindakan yang sama: mengunggah file. Oleh karena itu, saya mengusulkan untuk membuat fungsi terpisah universal untuk kedua metode, di mana kami akan mentransfer file, dan itu sudah akan memutuskan apa yang harus dilakukan dengan mereka. Kami akan menyebutnya
sendFiles()
, tetapi kami akan menjelaskannya sedikit nanti. Untuk memulai, tangani acara
drop
:
dropZone.on('drop', function(e) { dropZone.removeClass('dragover'); let files = e.originalEvent.dataTransfer.files; sendFiles(files); });
Pertama,
.dragover
hapus kelas
dropZone
dari
dropZone
. Kemudian kita mendapatkan array yang berisi file-file. Jika Anda menggunakan jQuery, maka
e.originalEvent.dataTransfer.files
adalah
e.originalEvent.dataTransfer.files
, jika Anda menulis dalam JS murni, maka
e.dataTransfer.files
. Baiklah, lalu kita meneruskan array ke fungsi kita yang belum terealisasi.
Sekarang kita akan menyelesaikan metode pemuatan melalui
input[type=file]
:
$('#file-input').change(function() { let files = this.files; sendFiles(files); });
Kami melacak
change
acara pada tombol pemilihan file, kami mendapatkan array melalui
this.files
dan mengirimkannya ke fungsi.
Mengirim file melalui AJAX
Langkah terakhir - deskripsi fungsi pemrosesan file - adalah unik untuk semua orang. Ini akan langsung tergantung pada tujuan yang Anda kejar. Sebagai contoh, saya akan menunjukkan cara mengirim file ke server melalui AJAX.
Misalkan kita membuat bidang untuk mengunggah foto. Kami tidak ingin hal lain sampai ke server kami, jadi kami akan memutuskan jenis file: biarlah PNG dan JPEG. Anda juga perlu mengatur ukuran maksimum satu foto yang dapat dikirim pengguna. Terbatas hingga lima megabita. Kami mulai menggambarkan fungsi kami:
function sendFiles(files) { let maxFileSize = 5242880; let Data = new FormData(); $(files).each(function(index, file) { if ((file.size <= maxFileSize) && ((file.type == 'image/png') || (file.type == 'image/jpeg'))) { Data.append('images[]', file); } }); };
Dalam variabel
maxFileSize
ukuran file maksimum yang akan kami kirim ke server.
FormData()
, kita akan membuat objek baru dari kelas
FormData
, yang memungkinkan kita untuk menghasilkan set pasangan nilai kunci. Objek seperti itu dapat dengan mudah dikirim melalui AJAX. Selanjutnya, kita menggunakan jQuery
.each
construct untuk array
files
, yang akan menerapkan fungsi yang kita atur untuk setiap elemennya. Jumlah elemen dan elemen itu sendiri akan diteruskan sebagai argumen ke fungsi, yang akan kami proses masing-masing sebagai
index
dan
file
. Dalam fungsi itu sendiri, kami akan memeriksa file terhadap kriteria kami: ukurannya kurang dari lima megabyte, dan tipenya PNG atau JPEG. Jika file lulus tes, lalu tambahkan ke objek
FormData
kami dengan memanggil fungsi
append()
. Kuncinya adalah string
'photos[]'
, tanda kurung di ujung yang menunjukkan bahwa itu adalah array di mana ada beberapa objek. Objek itu sendiri akan berupa
file
.
Sekarang semuanya siap untuk mengirim file melalui AJAX. Tambahkan baris berikut ke fungsi kami:
$.ajax({ url: dropZone.attr('action'), type: dropZone.attr('method'), data: Data, contentType: false, processData: false, success: function(data) { alert(' '); } });
Sebagai
url
dan
type
parameter
type
kami mengindikasikan nilai atribut
action
dan
method
dari
input[type=file]
masing-masing. Untuk melewati AJAX kita akan menjadi objek
Data
. Parameter
contentType: false
dan
processData: false
diperlukan agar browser tidak secara tidak sengaja menerjemahkan file kami ke dalam beberapa format lain. Dalam parameter
success
, kami menentukan fungsi yang akan dieksekusi jika file berhasil ditransfer ke server. Isinya tergantung pada imajinasi Anda, tetapi saya akan membatasi diri pada output sederhana dari sebuah pesan tentang unduhan yang berhasil.
Selamat, sekarang Anda dapat membuat bidang unggahan file Anda sendiri! Tentu saja, saya tidak memposisikan metode saya sebagai satu-satunya yang benar dan yang benar. Tujuan saya adalah menunjukkan jalan umum untuk menyelesaikan masalah ini, terutama cocok untuk pemula. Jika Anda berpikir bahwa di suatu tempat Anda bisa melakukan sesuatu yang lebih baik - tulis di komentar, kami akan membahas!
Itu saja. Terima kasih atas perhatian anda!
Unduh:
- Versi final
- Masalah fokus
- Masalah berkedip-kedip
Sentuh:
- Versi final
- Masalah fokus
- Masalah berkedip-kedip