DEV Community

steven chao
steven chao

Posted on

Lightweight Date Picker Component

Lightweight Date Picker Component: From Sudoku100 Project Practice

Project Background

This date picker component was originally developed for my Sudoku100 project — a free, online sudoku game platform for sudoku enthusiasts of all skill levels. If you're interested in sudoku or looking for a fun online puzzle game, feel free to check it out at: https://www.sudoku100.com

During the development of Sudoku100, I discovered the need for a lightweight, easy-to-use, and multi-language date picker to handle features like user birthdays and game record timestamps. After researching existing solutions, I found that they were either too bloated or not flexible enough, so I decided to implement one myself.

Core Technical Points

1. iOS-Style Wheel Picker

This is the core interaction method of the component. By mimicking the iOS system's wheel picker, it provides users with a smooth and intuitive date selection experience.

Technical Implementation:

  • Use CSS overflow-y: scroll to implement vertical scrolling
  • Listen to touch operations through touchstart, touchmove, touchend events
  • Implement inertia scrolling algorithm for more natural scrolling
  • Use transform: scale() and opacity to provide visual feedback for selected items
setupWheelScroll(wheel, type) {
    const itemHeight = 56;
    let isTouching = false;
    let velocity = 0;
    let lastTimestamp = 0;

    wheel.addEventListener('touchstart', (e) => {
        isTouching = true;
        velocity = 0;
        lastTimestamp = performance.now();
    }, { passive: true });

    wheel.addEventListener('touchmove', (e) => {
        if (!isTouching) return;
        e.preventDefault();
        // Calculate scrolling speed
        const deltaTime = currentTimestamp - lastTimestamp;
        if (deltaTime > 0) {
            velocity = deltaY / deltaTime;
        }
        // Update scroll position
        wheel.scrollTop += deltaY;
    }, { passive: false });

    wheel.addEventListener('touchend', () => {
        isTouching = false;
        // Apply inertia scrolling
        if (Math.abs(velocity) > 0.3) {
            this.applyInertiaScroll(wheel, type, itemHeight, velocity);
        } else {
            this.snapToItem(wheel, type, itemHeight);
        }
    }, { passive: true });
}
Enter fullscreen mode Exit fullscreen mode

2. Inertia Scrolling Algorithm

To make the scrolling experience closer to native applications, physics-based inertia scrolling was implemented:

  • Record the user's swipe speed
  • Use friction coefficient (0.95) to simulate physical deceleration
  • Automatically snap to the nearest option when speed falls below threshold
applyInertiaScroll(wheel, type, itemHeight, initialVelocity) {
    let velocity = initialVelocity;
    const friction = 0.95;
    const minVelocity = 0.1;

    const animate = () => {
        wheel.scrollTop += velocity * 16;
        velocity *= friction;

        if (Math.abs(velocity) > minVelocity) {
            requestAnimationFrame(animate);
        } else {
            this.snapToItem(wheel, type, itemHeight);
        }
    };

    animate();
}
Enter fullscreen mode Exit fullscreen mode

3. Multi-Language Support

The component supports 9 languages and can automatically adjust the date display order based on language:

  • Year-Month-Day order: Chinese, Japanese, Korean
  • Day-Month-Year order: English, Spanish, French, German, etc.

Language Adaptation Implementation:

generatePickerColumns(minYear, maxYear) {
    const langCode = window.commonTranslations?.lang || 'en';
    const isYearFirst = (langCode.startsWith('zh') || langCode === 'cn' ||
                        langCode.startsWith('ja') || langCode.startsWith('ko'));

    if (isYearFirst) {
        // Year, Month, Day
        return `
            <div class="picker-column">Year</div>
            <div class="picker-column">Month</div>
            <div class="picker-column">Day</div>
        `;
    } else {
        // Day, Month, Year
        return `
            <div class="picker-column">Day</div>
            <div class="picker-column">Month</div>
            <div class="picker-column">Year</div>
        `;
    }
}
Enter fullscreen mode Exit fullscreen mode

4. Smart Date Validation

Automatically handles changes in the number of days in different months:

getDaysInMonth(year, month) {
    return new Date(year, month + 1, 0).getDate();
}

updateDayOptions() {
    const daysInMonth = this.getDaysInMonth(this.currentYear, this.currentMonth);
    // Dynamically generate day options based on current year and month
}
Enter fullscreen mode Exit fullscreen mode

When users switch months, the component automatically updates the day options (e.g., February only has 28 or 29 days).

5. Responsive Design

Uses CSS media queries to adapt to different screen sizes:

@media (max-width: 768px) {
    .modal-content {
        max-width: 100%;
        border-radius: 16px 16px 0 0;
    }

    .picker-columns {
        width: 90%;
    }
}

@media (max-width: 480px) {
    .picker-columns {
        width: 80%;
    }
}
Enter fullscreen mode Exit fullscreen mode

6. Zero-Dependency Pure Native Implementation

The entire component doesn't depend on any third-party libraries, using only:

  • Native JavaScript (ES6+)
  • Native CSS3
  • Native DOM API

This makes the component:

  • Small in size (~10KB when minified)
  • Fast loading
  • Easy to integrate
  • No version conflict risks

Use Cases

1. Mobile Web Applications

Due to the iOS-style wheel interaction, it's particularly suitable for mobile scenarios:

  • User registration/login (selecting birthday)
  • Appointment systems (selecting date)
  • E-commerce applications (selecting delivery date)
  • Travel applications (selecting travel date)

2. Multi-Language Internationalization Projects

If your project needs to support multiple languages, this component works out of the box:

  • No need to implement date pickers separately for each language
  • Automatically adapts to different language date formats
  • Supports right-to-left (RTL) languages (like Arabic)

3. Lightweight Projects

For projects that prioritize performance:

  • Don't want to introduce large date libraries (like Moment.js, date-fns)
  • Only need basic year-month-day selection functionality
  • Have strict limitations on bundle size

4. Rapid Prototyping

Quickly implement functionality in the early stages of a project:

  • Simple API calls
  • No complex configuration required
  • Fast integration

Usage Examples

Basic Usage

// Open the date picker
window.datePicker.show(
    'Select Date',  // Title
    function(result) {
        console.log('Year:', result.year);
        console.log('Month:', result.month + 1);
        console.log('Day:', result.day);
    },
    {
        initialYear: new Date().getFullYear(),
        initialMonth: new Date().getMonth(),
        initialDay: new Date().getDate()
    }
);
Enter fullscreen mode Exit fullscreen mode

Setting Initial Date

window.datePicker.show(
    'Pick a Date',
    ({ year, month, day }) => {
        alert(`Selected: ${year}-${month + 1}-${day}`);
    },
    {
        initialYear: 2025,
        initialMonth: 0,  // January
        initialDay: 15
    }
);
Enter fullscreen mode Exit fullscreen mode

Multi-Language Support

// Set language
window.commonTranslations = {
    lang: 'zh',  // Chinese
    datePicker: {
        modalTitle: '选择日期',
        confirmButton: '确认'
    }
};

// Open the picker
window.datePicker.show(
    '选择日期',
    function(result) {
        console.log('Selected:', result.year, result.month + 1, result.day);
    }
);
Enter fullscreen mode Exit fullscreen mode

Custom Styling

/* Modify confirm button color */
.modal-confirm {
    background: #ff6b6b;
}

/* Modify selected item style */
.picker-wheel-item.selected .item-text {
    color: #ff6b6b;
    font-size: 22px;
}

/* Modify item height */
.picker-wheel-item {
    height: 60px;
}
Enter fullscreen mode Exit fullscreen mode

Technical Architecture

File Structure

Year-Month-Day-Date-Picker/
├── date-picker.js       # Main component logic
├── date-formatter.js    # Date formatting utility
├── date-picker.css      # Component styles
├── example.js           # Usage examples
└── index.html           # Demo page
Enter fullscreen mode Exit fullscreen mode

Core Class Design

DatePicker Class

  • show() - Display the picker
  • hide() - Hide the picker
  • confirm() - Confirm selection
  • selectYear/Month/Day() - Select specific values
  • updateDayOptions() - Update day options

DateFormatter Class

  • formatDate() - Format complete date
  • formatYearMonth() - Format year and month

Event Handling Flow

User Interaction → Touch Events → Calculate Speed → Inertia Scroll → Snap Alignment → Update State → Visual Feedback
Enter fullscreen mode Exit fullscreen mode

Performance Optimization

1. Event Throttling

Use requestAnimationFrame during scrolling to optimize rendering performance:

const animate = () => {
    wheel.scrollTop += velocity * 16;
    if (Math.abs(velocity) > minVelocity) {
        requestAnimationFrame(animate);
    }
};
Enter fullscreen mode Exit fullscreen mode

2. CSS Hardware Acceleration

Use transform and opacity to trigger GPU acceleration:

.picker-wheel-item {
    transition: opacity 0.3s ease, transform 0.3s ease;
}
Enter fullscreen mode Exit fullscreen mode

3. Lazy DOM Loading

Only create picker DOM when needed to avoid unnecessary memory usage.

Browser Compatibility

  • Chrome/Edge (latest)
  • Firefox (latest)
  • Safari (latest)
  • Mobile browsers (iOS Safari, Chrome Mobile)

Project Repository

GitHub: https://github.com/lvayywyg-cmyk/Year-Month-Day-Date-Picker

Conclusion

This date picker component started from an actual project requirement and, through careful design and optimization, has become a lightweight, easy-to-use, and feature-complete solution. It demonstrates how to implement complex interactive effects using native technologies, while also proving that sometimes "building your own wheel" is more suitable than using existing solutions.

If you're looking for a lightweight date picker, or want to learn how to implement iOS-style wheel interactions, feel free to try out this project. If you're interested in Sudoku100 puzzle games, you're also welcome to visit https://www.sudoku100.com to experience it!


Related Links:

Top comments (0)