You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

FullCalendar移动端:实现双击事件打开编辑模态框

FullCalendar移动端交互优化方案

问题场景

电脑端功能正常:鼠标悬停事件触发Tooltip显示,点击事件打开编辑模态框;但移动端点击事件时,Tooltip与编辑模态框会同时弹出,需求调整为移动端双击事件才打开编辑模态框。

原代码

JavaScript部分

document.addEventListener('DOMContentLoaded', function() {
if( /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ) {
    initialViews = 'dayGridMonth';
}else{
    initialViews = 'dayGridMonth';
}

var calendarEl = document.getElementById('calmanut');

  var today = moment().day();

  var calendar = new FullCalendar.Calendar(calendarEl, {

    headerToolbar: {
      left: 'prev,next today',
      center: 'title',
      right: 'dayGridMonth'
    },
    locale: "pt-br",
    buttonText:{
      today:    'Hoje',
      month:    'Mês',
    },

    navLinks: true,
      firstDay: today,
      initialView: initialViews,
      editable: true,
      selectable: true, 
      unselectAuto:true,
      eventOverlap: true,
      slotDuration: '00:30',
      allDaySlot : false,
      eventStartEditable: true,
      eventDurationEditable:false,
      longPressDelay: 0,
      nowIndicator: "true",
      expandRows: true,
      slotMinTime: '09:00',
      slotMaxTime: '18:00',
      contentHeight: 'auto',
      businessHours: {

        daysOfWeek: [ 1, 2, 3, 4, 5 ], 
    
      },
      eventDidMount: function (info) {
        $(info.el).popover({
            title: info.event.title,
            placement: 'top',
            trigger: 'hover', 
            content: 'Período: ' + info.event.groupId,
            container: 'body'
        });
      },
      
      events: [
      {
        title: 'All Day Event',
        start: '2023-11-17'
      },
      {
        title: 'Long Event',
        start: '2023-11-17',
        end: '2023-09-10',
        color: 'purple' // override!
      },
      {
        groupId: '999',
        title: 'Repeating Event',
        start: '2023-11-17T16:00:00'
      },
      {
        groupId: '999',
        title: 'Repeating Event',
        start: '2023-11-17T16:00:00'
      }
    ],
     eventClick:  function(info) {
        
        var eventObj = info.event; 
        tarperiod = eventObj.extendedProps.Tarefa;

        $('#ModalAddmanut1').modal("show");
        $('#ModalAddmanut1 #sidtaref').val(eventObj.extendedProps.Id);
        $('#ModalAddmanut1 #titletar1').val(eventObj.extendedProps.Tarefa);  
        $('#ModalAddmanut1 #cortaref1').val(eventObj.extendedProps.cor);
      },
    
  });

  calendar.render();
});

HTML部分

<link href="https://cdn.jsdelivr.net/npm/fullcalendar@5.9.0/main.min.css" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.19.1/moment.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/fullcalendar@5.9.0/main.min.js"></script>
<script src="https://unpkg.com/tooltip.js/dist/umd/tooltip.min.js"></script>
    
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/2.11.6/umd/popper.min.js"></script>
    
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/js/bootstrap.bundle.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/js/bootstrap.min.js"></script>

<div id='calmanut'></div>

<div class="modal fade" tabindex="-1" id="ModalAddmanut1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true" data-bs-backdrop="static" data-bs-keyboard="false">
        <div class="modal-dialog modal-lg" role="document">
            <div class="modal-content">
                <div class="modal-header">
                    <h5 class="modal-title" id="exampleModalLabel">ELIMINAR/ALTERAR PLANIFICAÇÃO DE TAREFAS</h5>
                    <button type="button" class="btn-close atualvisita1" data-bs-dismiss="modal" aria-label="Close"></button>
                </div>
                <div class="modal-body">
                    <form method="POST" class="row g-3 instper1">
                        <input type="hidden" class="form-control" id="sidtaref" name="sidtaref">
                        <div class="row">
                            <div class="col-8">
                                <div class="form-group">
                                    <label for="titletar1"> <strong>Tarefa</strong> </label>
                                    <select class="js-states form-control" name="titletar1" id="titletar1">
                                        <option></option>
                                        <option>Teste</option>
                                    </select>
                                </div>
                            </div>
                        </div>    
                        <div class="row">
                            <div class="col-6">
                                <div class="form-group">
                                    <label for="cortaref1"> <strong>Cor</strong> </label>
                                    <select class="js-states form-control" name="cortaref1" id="cortaref1">
                                        <option value=""></option>                            
                                        <option style="color:#000;" value="#F00">&#9724; Vermelho</option>
                                        <option style="color:#000;" value="#FFF">&#9724; Branco</option>
                                    </select>
                                </div>
                            </div>
                           </div> 
                    </form>
                </div>
                
                <div class="modal-footer">
                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Fechar</button>
                    <button type="button" class="btn btn-danger valtareff1" id="save-event">Eliminar</button>
                    <button type="button" class="btn btn-primary valtareff" id="save-event">Alterar</button>
                </div>
            </div>
        </div>
    </div>

解决方案

核心修改点

  1. 统一设备类型判断,提取isMobile变量
  2. 区分移动端与非移动端的Tooltip触发逻辑
  3. 为移动端实现双击触发模态框的交互逻辑

修改后的JavaScript代码

document.addEventListener('DOMContentLoaded', function() {
    // 统一判断移动端设备
    const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
    const initialViews = 'dayGridMonth'; // 简化原有重复赋值逻辑

    const calendarEl = document.getElementById('calmanut');
    const today = moment().day();

    const calendar = new FullCalendar.Calendar(calendarEl, {
        headerToolbar: {
            left: 'prev,next today',
            center: 'title',
            right: 'dayGridMonth'
        },
        locale: "pt-br",
        buttonText:{
            today:    'Hoje',
            month:    'Mês',
        },
        navLinks: true,
        firstDay: today,
        initialView: initialViews,
        editable: true,
        selectable: true, 
        unselectAuto:true,
        eventOverlap: true,
        slotDuration: '00:30',
        allDaySlot : false,
        eventStartEditable: true,
        eventDurationEditable:false,
        longPressDelay: 0,
        nowIndicator: "true",
        expandRows: true,
        slotMinTime: '09:00',
        slotMaxTime: '18:00',
        contentHeight: 'auto',
        businessHours: {
            daysOfWeek: [ 1, 2, 3, 4, 5 ], 
        },
        eventDidMount: function (info) {
            $(info.el).popover({
                title: info.event.title,
                placement: 'top',
                // 移动端用focus触发Tooltip,非移动端用hover
                trigger: isMobile ? 'focus' : 'hover',
                content: 'Período: ' + info.event.groupId,
                container: 'body'
            });
        },
        events: [
            {
                title: 'All Day Event',
                start: '2023-11-17'
            },
            {
                title: 'Long Event',
                start: '2023-11-17',
                end: '2023-09-10',
                color: 'purple'
            },
            {
                groupId: '999',
                title: 'Repeating Event',
                start: '2023-11-17T16:00:00'
            },
            {
                groupId: '999',
                title: 'Repeating Event',
                start: '2023-11-17T16:00:00'
            }
        ],
        eventClick: function(info) {
            const eventObj = info.event;
            const $eventEl = $(info.el);

            if (!isMobile) {
                // 非移动端直接打开编辑模态框
                openEditModal(eventObj);
                return;
            }

            // 移动端双击检测逻辑:300ms内连续点击两次视为双击
            const now = Date.now();
            const lastClickTime = $eventEl.data('lastClickTime') || 0;

            if (now - lastClickTime < 300) {
                // 触发双击事件,打开模态框
                openEditModal(eventObj);
                $eventEl.data('lastClickTime', 0);
            } else {
                // 第一次点击,仅触发Tooltip并记录点击时间
                $eventEl.focus();
                $eventEl.data('lastClickTime', now);
                // 超时未触发第二次点击则重置计时
                setTimeout(() => {
                    $eventEl.data('lastClickTime', 0);
                }, 300);
            }
        },
    });

    calendar.render();

    // 抽离模态框打开逻辑,提高代码复用性
    function openEditModal(eventObj) {
        tarperiod = eventObj.extendedProps.Tarefa;
        $('#ModalAddmanut1').modal("show");
        $('#ModalAddmanut1 #sidtaref').val(eventObj.extendedProps.Id);
        $('#ModalAddmanut1 #titletar1').val(eventObj.extendedProps.Tarefa);  
        $('#ModalAddmanut1 #cortaref1').val(eventObj.extendedProps.cor);
    }
});

效果说明

  • 非移动端:保持原有交互,鼠标悬停显示Tooltip,点击打开模态框
  • 移动端:第一次点击仅显示Tooltip,300ms内第二次点击才打开编辑模态框,避免误触

内容的提问来源于stack exchange,提问作者Bruno

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 04:03:09