🔥《刚刚问世》系列初窥篇-Java+Playwright自动化测试-32- 操作日历时间控件-下篇(详细教程)

1.简介

理想很丰满现实很骨感,在实际的工作和生活中我们在应用playwright实现web自动化时,经常会遇到处理日期控件点击问题,手工很简单,可以一个个点击日期控件选择需要的日期,但自动化执行过程中,完全模拟手工操作,这样的操作就有点难了或者是有些复杂啰嗦而且麻烦不过相对于selenium来说,playwright已经很好了。宏哥上一篇已经讲解了如何处理日历时间控件,但是有些网站不知道出于什么原因(宏哥猜测可能是安全的原因),对于第一种方法可能会遇到日历控件的输入框是包含readonly属性的情况,禁止输入文本。那么第一种方法就不适用了,但是只要我们换个思路然后稍微的变通地处理一下,就又可以使用了。

2.问题

宏哥第一种方法地思路就是把它当做输入框,直接输入日期即可,想法是很美好的,但是有时候实行起来却不执行,这个时候我们就要仔细去看看前端的代码了,代码如下:

ini 复制代码
<div class="col-lg-3 form-input">
  <input id="createTime" class="form-control" type="text" readonly="readonly" name="tatsudoDate" onclick="WdatePicker()" aria-required="true">
</div>

从上边的代码可以看出属性readonly人家根本不允许你输入,你就行不通了。

3.想法

既然这样了,我们就稍微变通一下,不要一条道走到黑。这个时候我们可以移除readonly的属性,问题就轻轻松松解决了,代码如下:

ini 复制代码
# 原生js,移除元素的readonly属性
js1 = 'document.getElementById("createTime").removeAttribute("readonly");'
page.evaluate(js1)
# 直接给输入框输入日期
js2 = 'document.getElementById("createTime").value="2023-11-11";'
page.evaluate(js2)

4.项目实战

网上找了半天也没有找到这样的例子,以前12306的日历是这种。最近升级了,已经不是这种了。不找了索性宏哥自己在本地做一个这样的小demo给小伙伴或者童鞋们来演示一下。

4.1代码准备

4.1.1前端HTML代码

前端HTML代码如下:

xml 复制代码
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title></title>
    <script src="dateJs.js"></script>
    <link rel="stylesheet" type="text/css" href="date.css">  
</head>
<body>
    <div id="wrapper" style="position: relative;top: 100px;left:600px;">
        <button class="button1"><a id="myAnchor" href="https://www.cnblogs.com/du-hong/">北京-宏哥</a></button></br>
        <input type="text" id="Dateinput" readonly=""/>
        <div class="calendar" id="calender" style="display: none;">
        </div>
    </div>
</body>
</html>

4.1.2CSS样式

HTML滑块CSS样式代码如下:

css 复制代码
* {
    margin: 0;
    padding: 0;
}

body {
    font-size: 13px;
}

.calendar {
    width: 330px;
}

.calendar .title {
    position: relative;
    width: 100%;
    height: 30px;
    line-height: 30px;
    background: #17a4eb;
}

.title div {
    position: absolute;
}

.prev {
    left: 10px;
}

.now {
    left: 40%;
}

.next {
    right: 10px;
}

input {
    height: 30px;
    width: 326px;
}

table {
    width: 100%;
    border-collapse: collapse;
}

table th {
    border: 1px solid #ccc;
}

table td {
    text-align: center;
    border: 1px solid #ccc;
}

.red {
    background-color: #a1cbdb;
}

.blue {
    background-color: #e4e3e3;
}

.button1 {
    background-color: #f44336;
    border: none;
    color: white;
    padding: 15px 32px;
    text-align: center;
    text-decoration: none;
    display: inline-block;
    font-size: 28px;
    margin-bottom: 100px;
    text-decoration: none;
    color: white;
}

#myAnchor {
    text-decoration: none;
    color: white;
}

4.1.3日历JS

日历JS代码如下:

java 复制代码
window.onload = function () {
    //获取日期 输入框
    var oInput = document.getElementById('Dateinput');
    //获取日历
    var oCalender = document.getElementById('calender');
    //获取当前日期
    var oDate = new Date();
    //获取当年 年
    var year = oDate.getFullYear();
    //获取当前 月
    var month = oDate.getMonth() + 1;

    //日历框不能重复创建
    var flag = false;
    //日期输入框 获取焦点时 加载日历
    oInput.onfocus = function () {
        showDate(year, month);
    }

    //显示日历
    function showDate(year, month) {
        if (false == flag) {
            //1.日历标题
            var oTitle = document.createElement('div');
            oTitle.className = 'title';

            //1.1日历标题文本
            var prevM = 0;
            var nextM = 0;

            prevM = month - 1;
            nextM = month + 1;

            //当月份为1时 上一个月为12
            if (month == 1) {
                prevM = 12;
            }//当月份为12时 下一个月为1
            else if (month == 12) {
                nextM = 1;
            }

            var titleHtml = "";
            titleHtml += '<div class="prev" id="prev"><span>';
            titleHtml += prevM + '</span>月</div>';
            titleHtml += '<div class="now">';
            titleHtml += '<span class="span">';
            titleHtml += year;
            titleHtml += '</span>年';
            titleHtml += '<span class="span">' + month;
            titleHtml += '</span>月</div>';
            titleHtml += '<div class="next" id="next"><span>';
            titleHtml += nextM + '</span>月</div>';

            oTitle.innerHTML = titleHtml;
            //将日历标题 拼接到日历
            oCalender.appendChild(oTitle);

            //1.2获取日历 表头元素(以便添加事件)
            var oSpans = oCalender.getElementsByTagName('span');
            var prevMonth = oSpans[0];
            var nextMonth = oSpans[3];
            var nowMonth = oSpans[2];
            var nowYear = oSpans[1];

            //2.创建星期 表头
            var otable = document.createElement('table');
            var othead = document.createElement('thead');
            var otr = document.createElement('tr');

            //2.1表头内容填充
            var arr = ['日', '一', '二', '三', '四', '五', '六'];
            for (var i = 0; i < arr.length; i++) {
                //创建th
                var oth = document.createElement('th');
                oth.innerHTML = arr[i];
                otr.appendChild(oth);
            }

            //2.2将表头加入到日历
            othead.appendChild(otr);
            otable.appendChild(othead);
            oCalender.appendChild(otable);

            //3.添加 当前日历 全部日期
            //3.1.先获得当期月 有多少天
            var dayNum = 0;
            if (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12) {
                dayNum = 31;
            } else if (month == 4 || month == 6 || month == 9 || month == 11) {
                dayNum = 30;
            } else if (month == 2 && isLeapYear(year)) {
                dayNum = 29;
            } else {
                dayNum = 28;
            }

            //3.2.创建 6行7列 日期容器
            var otbody = document.createElement('tbody');
            for (var i = 0; i < 6; i++) {
                var otr = document.createElement('tr');
                for (var j = 0; j < 7; j++) {
                    var otd = document.createElement('td');
                    otr.appendChild(otd);
                }
                otbody.appendChild(otr);
            }
            otable.appendChild(otbody);

            //3.3获得 1号对应的是星期几
            //3.3.1.将当月1号赋值给日期变量
            oDate.setFullYear(year);
            //注意 js日期的月份是从0 开始计算
            oDate.setMonth(month - 1);
            oDate.setDate(1);

            //3.3.2.计算1号在第一行日期容器中的位置,依次给日期容器填充内容
            //注意 js中 getDay方法是获取当前日期是星期几
            var week = oDate.getDay();
            var otds = oCalender.getElementsByTagName('td');
            for (var i = 0; i < dayNum; i++) {
                otds[i + week].innerHTML = i + 1;
            }


            //让当前日期显示红色、后面的显示蓝色
            showColor(otds);
            //给左右月份绑定点击事件
            monthEvent();
            //判断最后一行是否全为空
            lastTr(otds);
            flag = true;
            document.getElementById('calender').style.display = "block";
        }
    }

    //判断是否是闰年
    function isLeapYear(year) {
        if (year % 100 == 0 && year % 400 == 0) {
            return true;
        } else if (year % 100 != 0 && year % 4 == 0) {
            return true;
        } else {
            return false;
        }
    }

    //判断日期容器最后一行是否有值
    function lastTr(otds) {
        var flag = true;
        for (var i = 35; i < 42; i++) {
            if (otds[i].innerHTML != '') {
                flag = false;
            }
        }
        //全是空的
        if (flag) {
            for (var i = 35; i < 42; i++) {
                otds[i].style.display = 'none';
            }
        }
    }

    //当前日期显示红色、前面的显示灰色
    function showColor(otds) {
        //当前日期
        var nowday = new Date().getDate();
        var nowyear = new Date().getFullYear();
        var nowmonth = new Date().getMonth();

        var oCalendar = document.getElementById("calender");
        ospans = oCalendar.getElementsByTagName('span');
        var contralYear = ospans[1].innerHTML;
        var contralMonth = ospans[2].innerHTML;

        var oindex = 0;
        for (var i = 0; i < otds.length; i++) {
            if (nowday == otds[i].innerHTML && nowyear == contralYear && nowmonth + 1 == contralMonth) {
                otds[i].className = 'red';
                oindex = i;
            }
        }
    }

    //给左右月份绑定点击事件
    function monthEvent() {
        var oCalendar = document.getElementById("calender");
        var prevDiv = document.getElementById("prev");
        var nextDiv = document.getElementById("next");

        var prevMonth = prevDiv.getElementsByTagName("span");
        var nextMonth = nextDiv.getElementsByTagName("span");

        prevDiv.onclick = function () {
            flag = false;
            oCalendar.innerHTML = '';
            showDate(year, parseInt(prevMonth[0].innerHTML));
        }

        nextDiv.onclick = function () {
            flag = false;
            oCalendar.innerHTML = '';
            showDate(year, parseInt(nextMonth[0].innerHTML));
        }

    }
}

4.1.4日历demo演示

各种代码准备好以后,浏览器打开HTML代码,如下图所示:

5.趁热打铁

根据前边宏哥提出的想法,我们来看一下如何用脚本代码实现了。首先要移除HTML代码里的属性,我们就需要用到JavaScript的知识,这部分还是需要自己自学一下,灰常简单。移除后输入框就变成可以编辑的了,我们就又可以利用输入的方法就日期输入到日历控件中。

5.1代码设计

5.2参考代码

java 复制代码
package com.bjhg.playwright;

import com.microsoft.playwright.Browser;
import com.microsoft.playwright.BrowserContext;
import com.microsoft.playwright.BrowserType;
import com.microsoft.playwright.Page;
import com.microsoft.playwright.Playwright;

/**
 * @author 北京-宏哥
 * 
 * @公众号:北京宏哥(微信搜索,关注宏哥,提前解锁更多测试干货)
 * 
 * 《刚刚问世》系列初窥篇-Java+Playwright自动化测试-32- 操作日历时间控件-下篇(详细教程)
 *
 * 2025年08月08日
 */

public class Test_calendar {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        try (Playwright playwright = Playwright.create()) {
            //1.使用chromium浏览器,# 浏览器配置,设置以GUI模式启动Chrome浏览器(要查看浏览器UI,在启动浏览器时传递 headless=false 标志。您还可以使用 slowMo 来减慢执行速度。
            Browser browser = playwright.chromium().launch(new BrowserType.LaunchOptions().setHeadless(false).setSlowMo(500));
            //2.创建context
            BrowserContext context = browser.newContext();
            //创建page
            Page page = context.newPage();
            //3.浏览器访问demo
            page.navigate("file:///C:/Users/duhon/Desktop/test/Calendar.html");
            Thread.sleep(100);
            // 原生js,移除元素的readonly属性
            String js1 = "document.getElementById('Dateinput').removeAttribute('readonly');";
            page.evaluate(js1);
            // 直接给输入框输入日期
//            String js2 = "document.getElementById('Dateinput').value='2025-10-01';";
//            page.evaluate(js2);
            page.locator("#Dateinput").fill("2025-10-01");
            Thread.sleep(500);
            System.out.println("Test Pass");
            //5.关闭page
            page.close();
            //6.关闭browser
            browser.close();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

5.3运行代码

1.运行代码,右键Run As->Java Application,就可以看到控制台输出,如下图所示:

2.运行代码后电脑端的浏览器的动作(日历控件中成功输入25年的国庆节日期)。如下图所示:

6.问题答疑

之前在python的语言中利用playwright操作日历时间控件,有小伙伴或者童鞋们给宏哥留言:不去掉直接输入不行吗?这问题问的如果行,宏哥还有必要写一篇文章进行说明如何处理吗?而且这也非常简单啊,你直接将去掉的代码自己注释掉,运行代码实践一下就很清楚了。

既然有人问道,那么宏哥就在这里演示一下,将代码注释掉,然后运行代码。这里要分两种情况来处理哈!

6.1第一种情况

代码中不用JavaScript脚本处理,直接还按上一篇宏哥的思路将其当作输入框来处理,直接用Java脚本来操作处理。不出意料的意料之中就会报错哦!

6.1.1代码设计

6.1.2参考代码

java 复制代码
package com.bjhg.playwright;

import com.microsoft.playwright.Browser;
import com.microsoft.playwright.BrowserContext;
import com.microsoft.playwright.BrowserType;
import com.microsoft.playwright.Page;
import com.microsoft.playwright.Playwright;

/**
 * @author 北京-宏哥
 * 
 * @公众号:北京宏哥(微信搜索,关注宏哥,提前解锁更多测试干货)
 * 
 * 《刚刚问世》系列初窥篇-Java+Playwright自动化测试-32- 操作日历时间控件-下篇(详细教程)
 *
 * 2025年08月08日
 */

public class Test_calendar {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        try (Playwright playwright = Playwright.create()) {
            //1.使用chromium浏览器,# 浏览器配置,设置以GUI模式启动Chrome浏览器(要查看浏览器UI,在启动浏览器时传递 headless=false 标志。您还可以使用 slowMo 来减慢执行速度。
            Browser browser = playwright.chromium().launch(new BrowserType.LaunchOptions().setHeadless(false).setSlowMo(500));
            //2.创建context
            BrowserContext context = browser.newContext();
            //创建page
            Page page = context.newPage();
            //3.浏览器访问demo
            page.navigate("file:///C:/Users/duhon/Desktop/test/Calendar.html");
            Thread.sleep(100);
            // 原生js,移除元素的readonly属性
//            String js1 = "document.getElementById('Dateinput').removeAttribute('readonly');";
//            page.evaluate(js1);
            // 直接给输入框输入日期
//            String js2 = "document.getElementById('Dateinput').value='2025-10-01';";
//            page.evaluate(js2);
            page.locator("#Dateinput").fill("2025-10-01");
            Thread.sleep(500);
            System.out.println("Test Pass");
            //5.关闭page
            page.close();
            //6.关闭browser
            browser.close();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

6.1.3运行代码

1.运行代码,右键Run As->Java Application,就可以看到控制台输出报错,大致意思:需要等待的元素是可见、可启用并可编辑,然而实际上元素不可编辑 - 等待中...,然后就超时了。如下图所示:

2.运行代码后电脑端的浏览器的动作(日历控件中没有输入25年的国庆节日期)。如下图所示:、

6.2第二种情况

代码中用JavaScript脚本处理,直接还按上一篇宏哥的思路将其当作输入框来处理,直接用JavaScript脚本来操作处理。这种情况不用问,应该可以成功,你都可以将其属性移除,输入值就更不在话下了,不信的话来跟随宏哥一起来实践证明一下。

6.2.1代码设计

6.2.2参考代码

ini 复制代码
package com.bjhg.playwright;

import com.microsoft.playwright.Browser;
import com.microsoft.playwright.BrowserContext;
import com.microsoft.playwright.BrowserType;
import com.microsoft.playwright.Page;
import com.microsoft.playwright.Playwright;

/**
 * @author 北京-宏哥
 * 
 * @公众号:北京宏哥(微信搜索,关注宏哥,提前解锁更多测试干货)
 * 
 * 《刚刚问世》系列初窥篇-Java+Playwright自动化测试-32- 操作日历时间控件-下篇(详细教程)
 *
 * 2025年08月08日
 */

public class Test_calendar {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        try (Playwright playwright = Playwright.create()) {
            //1.使用chromium浏览器,# 浏览器配置,设置以GUI模式启动Chrome浏览器(要查看浏览器UI,在启动浏览器时传递 headless=false 标志。您还可以使用 slowMo 来减慢执行速度。
            Browser browser = playwright.chromium().launch(new BrowserType.LaunchOptions().setHeadless(false).setSlowMo(500));
            //2.创建context
            BrowserContext context = browser.newContext();
            //创建page
            Page page = context.newPage();
            //3.浏览器访问demo
            page.navigate("file:///C:/Users/duhon/Desktop/test/Calendar.html");
            Thread.sleep(100);
            // 原生js,移除元素的readonly属性
//            String js1 = "document.getElementById('Dateinput').removeAttribute('readonly');";
//            page.evaluate(js1);
            // 直接给输入框输入日期
            String js2 = "document.getElementById('Dateinput').value='2025-10-01';";
            page.evaluate(js2);
//            page.locator("#Dateinput").fill("2025-10-01");
            Thread.sleep(500);
            System.out.println("Test Pass");
            //5.关闭page
            page.close();
            //6.关闭browser
            browser.close();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

6.2.3运行代码

1.运行代码,右键Run As->Java Application,就可以看到控制台输出,如下图所示:

2.运行代码后电脑端的浏览器的动作(日历控件中成功输入25年的国庆节日期)。如下图所示:

7.小结

7.1情况说明

之前在Python+Playwright系列中处理日历控件的时候,在代码中写了两个JavaScript脚本(js1和js2),只需要js2的脚本就可以成功实现,js1属于画蛇添足了,同时这里感谢一下提出疑问的那位小伙伴或者童鞋。如下图所示:

今天主要讲解和分享在工作中遇到日历控件的输入框中包含readonly属性,不允许用户编辑输入日期。我们如何处理和操作,大致两种方法:一种就是playwright和JavaScript混合处理操作,另外一种就是纯JavaScript处理。都是比较简单的。难点就是你要属性JavaScript的脚本语言去处理前端页面的元素。

好了,时间不早了,今天就分享到这里,感谢大家耐心的阅读,这一篇内容其实是为后边文章的JavaScript的调用做一下铺垫和入门。

相关推荐
Java中文社群8 分钟前
快看!百度提前批的面试难度,你能拿下吗?
java·后端·面试
不远处的小阿秋9 分钟前
2025年,前端还需要虚拟DOM吗
前端
DcTbnk15 分钟前
tailwindcss、postcss、autoprefixer,这三个分别是干嘛的
前端
丨千纸鹤丨16 分钟前
Tomcat
java·tomcat
zReadonly20 分钟前
antdv@4.x在360极速浏览器兼容解决办法
前端
yede23 分钟前
页面中模块通讯简单实现
前端·javascript·html
发发发发88829 分钟前
leetcode 674.最长连续递增序列
java·数据结构·算法·leetcode·动态规划·最长连续递增序列
前端Hardy30 分钟前
HTML&CSS&JS:超级惊艳的全屏图片轮播效果
前端·javascript·css
用户975142815002136 分钟前
实现页面在移动端自适应的方法总结
前端·css
回忆是昨天里的海1 小时前
3.3.2_1栈在表达式求值中的应用(上)
java··后缀表达式·前缀表达式