
Security News
Axios Maintainer Confirms Social Engineering Attack Behind npm Compromise
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.
thai-address-select
Advanced tools
Linked province → district → subdistrict dropdowns for Thailand (Vanilla JS).
A lightweight TypeScript library for creating linked dropdowns for Thai address selection (Province → District → Subdistrict) with automatic postal code retrieval.
npm install thai-address-select
<!DOCTYPE html>
<html>
<body>
<select id="province"></select>
<select id="district"></select>
<select id="subDistrict"></select>
<input id="zipCode" readonly>
<script type="module">
import { ThaiAddressSelect } from './node_modules/thai-address-select/dist/src/index.js';
import { loadData } from './node_modules/thai-address-select/dist/src/utils/filterAddress.js';
// 1. Load data first
await loadData();
// 2. Initialize
const thaiAddress = new ThaiAddressSelect({
provinceEl: document.getElementById("province"),
districtEl: document.getElementById("district"),
subDistrictEl: document.getElementById("subDistrict")
});
// 3. Listen for changes
thaiAddress.addEventListener("selectChange", (e) => {
document.getElementById("zipCode").value = e.detail.zip_code || "";
console.log(e.detail); // { province, district, subDistrict, zip_code }
});
</script>
</body>
</html>
Note: Make sure to run this through a local server (e.g.,
npx serve .) because ES modules don't work withfile://protocol.
"use client";
import { useEffect, useRef, useState } from 'react';
export default function AddressForm() {
const provinceRef = useRef<HTMLSelectElement>(null);
const districtRef = useRef<HTMLSelectElement>(null);
const subDistrictRef = useRef<HTMLSelectElement>(null);
const [zipCode, setZipCode] = useState('');
useEffect(() => {
let thaiAddress: any;
const init = async () => {
// Dynamic import to avoid SSR issues
const { ThaiAddressSelect } = await import('thai-address-select');
const { loadData } = await import('thai-address-select');
await loadData();
if (provinceRef.current && districtRef.current && subDistrictRef.current) {
thaiAddress = new ThaiAddressSelect({
provinceEl: provinceRef.current,
districtEl: districtRef.current,
subDistrictEl: subDistrictRef.current
});
thaiAddress.addEventListener('selectChange', (e: any) => {
setZipCode(e.detail.zip_code || '');
});
}
};
init();
return () => thaiAddress?.destroy();
}, []);
return (
<div>
<select ref={provinceRef} className="w-full p-2 border rounded" />
<select ref={districtRef} className="w-full p-2 border rounded" />
<select ref={subDistrictRef} className="w-full p-2 border rounded" />
<input value={zipCode} readOnly className="w-full p-2 border rounded bg-gray-100" />
</div>
);
}
interface ThaiAddressSelectOptions {
provinceEl: HTMLSelectElement; // Required: Province dropdown element
districtEl: HTMLSelectElement; // Required: District dropdown element
subDistrictEl: HTMLSelectElement; // Required: Subdistrict dropdown element
placeholder?: { // Optional: Custom placeholder text
province?: string;
district?: string;
subDistrict?: string;
};
initialValue?: { // Optional: Set initial values
province?: string;
district?: string;
subDistrict?: string;
};
}
getValue()
Returns current selected values including postal code.
const value = thaiAddress.getValue();
// Returns: { province?: string, district?: string, subDistrict?: string, zip_code?: string }
setValue(value)
Set values programmatically.
thaiAddress.setValue({
province: "กรุงเทพมหานคร",
district: "เขตบางรัก",
subDistrict: "มหาพฤฒาราม"
});
destroy()
Remove event listeners and clean up.
thaiAddress.destroy();
selectChange
Fired when all dropdowns (province, district, subdistrict) have values.
thaiAddress.addEventListener("selectChange", (e) => {
console.log(e.detail.province); // "กรุงเทพมหานคร"
console.log(e.detail.district); // "เขตบางรัก"
console.log(e.detail.subDistrict); // "มหาพฤฒาราม"
console.log(e.detail.zip_code); // "10500"
});
provinceChange, districtChange, subDistrictChange
Fired when individual dropdown changes.
thaiAddress.addEventListener("provinceChange", (e) => {
console.log(e.detail.province);
});
These functions can be used independently without creating a ThaiAddressSelect instance.
loadData()
Must be called before using any other functions.
import { loadData } from 'thai-address-select';
await loadData();
getProvinces()
Returns array of all province names (sorted in Thai alphabetical order).
import { getProvinces } from 'thai-address-select';
const provinces = getProvinces();
// ["กระบี่", "กรุงเทพมหานคร", ...]
getDistricts(province)
Returns array of district names for a given province.
import { getDistricts } from 'thai-address-select';
const districts = getDistricts("กรุงเทพมหานคร");
// ["เขตบางกอกน้อย", "เขตบางกอกใหญ่", ...]
getSubDistricts(province, district)
Returns array of subdistrict names for a given province and district.
import { getSubDistricts } from 'thai-address-select';
const subDistricts = getSubDistricts("กรุงเทพมหานคร", "เขตบางรัก");
// ["มหาพฤฒาราม", "สีลม", "สุริยวงศ์", ...]
getzip_code(province, district, subDistrict)
Returns postal code for a given address.
import { getzip_code } from 'thai-address-select';
const zipCode = getzip_code("กรุงเทพมหานคร", "เขตบางรัก", "มหาพฤฒาราม");
// "10500"
const thaiAddress = new ThaiAddressSelect({
provinceEl: provinceEl,
districtEl: districtEl,
subDistrictEl: subDistrictEl,
placeholder: {
province: "Select Province",
district: "Select District",
subDistrict: "Select Subdistrict"
}
});
const thaiAddress = new ThaiAddressSelect({
provinceEl: provinceEl,
districtEl: districtEl,
subDistrictEl: subDistrictEl,
initialValue: {
province: "กรุงเทพมหานคร",
district: "เขตบางรัก",
subDistrict: "มหาพฤฒาราม"
}
});
The library includes a complete database of Thai administrative divisions:
rratchapol
ไลบรารี TypeScript น้ำหนักเบาสำหรับสร้าง dropdown เลือกที่อยู่ไทยแบบเชื่อมโยง (จังหวัด → อำเภอ → ตำบล) พร้อมดึงรหัสไปรษณีย์อัตโนมัติ
npm install thai-address-select
<!DOCTYPE html>
<html>
<body>
<select id="province"></select>
<select id="district"></select>
<select id="subDistrict"></select>
<input id="zipCode" readonly>
<script type="module">
import { ThaiAddressSelect } from './node_modules/thai-address-select/dist/src/index.js';
import { loadData } from './node_modules/thai-address-select/dist/src/utils/filterAddress.js';
// 1. โหลดข้อมูลก่อน
await loadData();
// 2. สร้าง instance
const thaiAddress = new ThaiAddressSelect({
provinceEl: document.getElementById("province"),
districtEl: document.getElementById("district"),
subDistrictEl: document.getElementById("subDistrict")
});
// 3. รอรับการเปลี่ยนแปลง
thaiAddress.addEventListener("selectChange", (e) => {
document.getElementById("zipCode").value = e.detail.zip_code || "";
console.log(e.detail); // { province, district, subDistrict, zip_code }
});
</script>
</body>
</html>
หมายเหตุ: ต้องรันผ่าน local server (เช่น
npx serve .) เพราะ ES modules ใช้งานไม่ได้กับfile://protocol
"use client";
import { useEffect, useRef, useState } from 'react';
export default function AddressForm() {
const provinceRef = useRef<HTMLSelectElement>(null);
const districtRef = useRef<HTMLSelectElement>(null);
const subDistrictRef = useRef<HTMLSelectElement>(null);
const [zipCode, setZipCode] = useState('');
useEffect(() => {
let thaiAddress: any;
const init = async () => {
// Dynamic import เพื่อหลีกเลี่ยงปัญหา SSR
const { ThaiAddressSelect } = await import('thai-address-select');
const { loadData } = await import('thai-address-select');
await loadData();
if (provinceRef.current && districtRef.current && subDistrictRef.current) {
thaiAddress = new ThaiAddressSelect({
provinceEl: provinceRef.current,
districtEl: districtRef.current,
subDistrictEl: subDistrictRef.current
});
thaiAddress.addEventListener('selectChange', (e: any) => {
setZipCode(e.detail.zip_code || '');
});
}
};
init();
return () => thaiAddress?.destroy();
}, []);
return (
<div>
<select ref={provinceRef} className="w-full p-2 border rounded" />
<select ref={districtRef} className="w-full p-2 border rounded" />
<select ref={subDistrictRef} className="w-full p-2 border rounded" />
<input value={zipCode} readOnly className="w-full p-2 border rounded bg-gray-100" />
</div>
);
}
interface ThaiAddressSelectOptions {
provinceEl: HTMLSelectElement; // จำเป็น: Element dropdown จังหวัด
districtEl: HTMLSelectElement; // จำเป็น: Element dropdown อำเภอ
subDistrictEl: HTMLSelectElement; // จำเป็น: Element dropdown ตำบล
placeholder?: { // ไม่จำเป็น: ข้อความ placeholder
province?: string;
district?: string;
subDistrict?: string;
};
initialValue?: { // ไม่จำเป็น: ค่าเริ่มต้น
province?: string;
district?: string;
subDistrict?: string;
};
}
getValue()
คืนค่าที่เลือกปัจจุบัน รวมรหัสไปรษณีย์
const value = thaiAddress.getValue();
// คืนค่า: { province?: string, district?: string, subDistrict?: string, zip_code?: string }
setValue(value)
ตั้งค่าผ่านโค้ด
thaiAddress.setValue({
province: "กรุงเทพมหานคร",
district: "เขตบางรัก",
subDistrict: "มหาพฤฒาราม"
});
destroy()
ลบ event listeners และทำความสะอาด
thaiAddress.destroy();
selectChange
ทำงานเมื่อ dropdown ทั้งหมด (จังหวัด อำเภอ ตำบล) มีค่า
thaiAddress.addEventListener("selectChange", (e) => {
console.log(e.detail.province); // "กรุงเทพมหานคร"
console.log(e.detail.district); // "เขตบางรัก"
console.log(e.detail.subDistrict); // "มหาพฤฒาราม"
console.log(e.detail.zip_code); // "10500"
});
provinceChange, districtChange, subDistrictChange
ทำงานเมื่อ dropdown แต่ละตัวเปลี่ยนแปลง
thaiAddress.addEventListener("provinceChange", (e) => {
console.log(e.detail.province);
});
ฟังก์ชันเหล่านี้สามารถใช้งานแยกโดยไม่ต้องสร้าง instance ของ ThaiAddressSelect
loadData()
ต้องเรียกก่อนใช้ฟังก์ชันอื่นๆ
import { loadData } from 'thai-address-select';
await loadData();
getProvinces()
คืน array ชื่อจังหวัดทั้งหมด (เรียงตามตัวอักษรไทย)
import { getProvinces } from 'thai-address-select';
const provinces = getProvinces();
// ["กระบี่", "กรุงเทพมหานคร", ...]
getDistricts(province)
คืน array ชื่ออำเภอของจังหวัดที่ระบุ
import { getDistricts } from 'thai-address-select';
const districts = getDistricts("กรุงเทพมหานคร");
// ["เขตบางกอกน้อย", "เขตบางกอกใหญ่", ...]
getSubDistricts(province, district)
คืน array ชื่อตำบลของจังหวัดและอำเภอที่ระบุ
import { getSubDistricts } from 'thai-address-select';
const subDistricts = getSubDistricts("กรุงเทพมหานคร", "เขตบางรัก");
// ["มหาพฤฒาราม", "สีลม", "สุริยวงศ์", ...]
getzip_code(province, district, subDistrict)
คืนรหัสไปรษณีย์ของที่อยู่ที่ระบุ
import { getzip_code } from 'thai-address-select';
const zipCode = getzip_code("กรุงเทพมหานคร", "เขตบางรัก", "มหาพฤฒาราม");
// "10500"
const thaiAddress = new ThaiAddressSelect({
provinceEl: provinceEl,
districtEl: districtEl,
subDistrictEl: subDistrictEl,
placeholder: {
province: "เลือกจังหวัด",
district: "เลือกอำเภอ",
subDistrict: "เลือกตำบล"
}
});
const thaiAddress = new ThaiAddressSelect({
provinceEl: provinceEl,
districtEl: districtEl,
subDistrictEl: subDistrictEl,
initialValue: {
province: "กรุงเทพมหานคร",
district: "เขตบางรัก",
subDistrict: "มหาพฤฒาราม"
}
});
ไลบรารีรวมฐานข้อมูลการแบ่งเขตการปกครองของไทยครบถ้วน:
rratchapol
FAQs
Linked province → district → subdistrict dropdowns for Thailand (Vanilla JS).
The npm package thai-address-select receives a total of 1 weekly downloads. As such, thai-address-select popularity was classified as not popular.
We found that thai-address-select demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.

Security News
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.