This article assumes you have a basic understanding of browser F12 tools, bookmarks, and Tampermonkey. If you have questions, please leave a comment.
Background At the end of every term, the educational administration system requires filling out satisfaction questionnaires for several course teachers. Common system restrictions:
- All indicators for each teacher cannot be completely identical—there must be 1 item that is different.
- The rating control is in the form of
html <select name="DataGrid1$ctl02$JS1">…</select>ASP.NET mixes row and column numbers intoname / id. - The number of columns (teachers) may be ≥ 1, and column numbers can be
JS1or written as lowercasejs1.
Goal
For each teacher: Keep only 1 indicator as
A(or any diffVal you want), and the rest asB(defaultVal); Never select too many, never select too few.
0. Quick Start
javascript:(()=>{const d="B",a="A";const w=(s,v)=>{if(s.value!=v){s.value=v;["change","input","blur"].forEach(e=>s.dispatchEvent(new Event(e,{bubbles:!0})))}};const g=s=>((s.name.match(/\$[Jj][Ss]\d+/)||s.id.match(/DataGrid1_[Jj][Ss]\d+/)||[])[0]||"").toUpperCase();const f=t=>{const S=[...t.querySelectorAll('select[id^="DataGrid1_"],select[name^="DataGrid1$"]')];if(!S.length)return!1;const M={};S.forEach(s=>{const k=g(s);k&&(M[k]??=[]).push(s)});Object.values(M).forEach(col=>{col.forEach(x=>w(x,d));w(col[Math.random()*col.length|0],a);col.filter(x=>x.value===a).slice(1).forEach(x=>w(x,d))});return!0};const all=t=>{let ok=f(t);t.querySelectorAll("iframe").forEach(fr=>{try{fr.contentDocument&&(ok=all(fr.contentDocument)||ok)}catch{}});return ok};all(document)})();void 0;
- Save the entire line as a Bookmark (Bookmarklet).
- Open the evaluation page → Click the bookmark → Finished within three seconds.
The following text will break down the script logic in detail and provide two alternative usage methods: one-time console execution and Tampermonkey automation.
1. Core Principles
| Step | Key Point | Explanation |
|---|---|---|
| Element Positioning | querySelectorAll('select[id^="DataGrid1_"], select[name^="DataGrid1$"]') | ASP.NET writes double copies of id and name; choosing one covers the vast majority of school systems. |
| Teacher Grouping | Regex capture JS\d+ / js\d+ | Columnar layout: same column means same teacher, just need to identify the column number. Solves mixed case writing via toUpperCase(). |
| Batch Assignment | defaultVal full coverage → Randomly pick 1 item to change to diffVal | First rate same level, then rate different level, ensuring neatness. |
| Secondary Validation | filter(sel => sel.value === diffVal) | If manual user changes cause multiple As in a column, the script automatically changes the extras back to the default score. |
| Event Triggering | change + input + blur | Old educational systems often listen to one of these; dispatch all at once to ensure "manual operation" traces. |
| iframe Recursion | document.querySelectorAll("iframe") | Some systems embed the rating table in a sub-frame; recursive traversal ensures 100% coverage. |
In a nutshell: The script is—Position → Group → Cover → Rate Differently → Validate Rating → Notify.
2. Three Usage Methods
2.1 Console Temporary Script
- Enter the evaluation page, scroll to the bottom to ensure all controls are loaded.
F12 → Console, paste the full script below, press Enter.- After
✅pops up, click "Submit" directly.
(()=>{
/* ========= 1. 自定义 ========= */
const defaultVal = "B"; // 其它评分
const diffVal = "A"; // 每位教师唯一不同评分
const maxRetry = 10; // 最多重试
const retryDelay = 400; // 间隔 ms
/* ============================ */
/* 给 <select> 赋值并触发事件 */
function setSelect(sel,val){
if(sel.value===val) return;
sel.value = val;
["change","input","blur"].forEach(e=>
sel.dispatchEvent(new Event(e,{bubbles:true}))
);
}
/* 提取列号:JS1 / js1 / JS10 … → 统一成大写 JS1、JS10 */
function getColKey(sel){
// 1) 先尝试 name="…$ctl02$JS3"
let m = sel.name.match(/\$([Jj][Ss]\d+)\b/);
if(m) return m[1].toUpperCase();
// 2) 再尝试 id="DataGrid1_JS3_0"
m = sel.id.match(/DataGrid1_([Jj][Ss]\d+)_/);
if(m) return m[1].toUpperCase();
return null; // 无法识别
}
/* 处理单个 document */
function fillOnce(doc){
const selects=[...doc.querySelectorAll(
'select[id^="DataGrid1_"], select[name^="DataGrid1$"]'
)];
if(!selects.length) return false;
const groups={}; // {JS1:[sel…], JS2:[sel…]}
selects.forEach(sel=>{
const key=getColKey(sel);
if(key) (groups[key] ||= []).push(sel);
});
// 若只识别出 0/1 列,提前报错方便排查
if(Object.keys(groups).length<=1){
console.warn("⚠️ 只识别到 1 列教师,请检查 getColKey 正则是否匹配。");
}
/* 每位教师:全部 defaultVal → 抽 1 个改 diffVal → 二次校验 */
Object.values(groups).forEach(col=>{
col.forEach(s=>setSelect(s,defaultVal)); // 1) 全填 B
const lucky = col[Math.random()*col.length|0]; // 2) 抽一个改 A
setSelect(lucky,diffVal);
const diffList = col.filter(s=>s.value===diffVal);// 3) 若意外多于 1 个
diffList.slice(1).forEach(s=>setSelect(s,defaultVal));
});
return true;
}
/* 递归遍历 iframe */
function fillAll(doc=document){
let ok=fillOnce(doc);
doc.querySelectorAll("iframe").forEach(f=>{
try{f.contentDocument&&(ok=fillAll(f.contentDocument)||ok);}catch{}
});
return ok;
}
/* 自动重试,确保元素加载完 */
let tries=0;
function runner(){
if(fillAll()){
const sta=[...document.querySelectorAll(
'select[id^="DataGrid1_"], select[name^="DataGrid1$"]'
)].reduce((m,s)=>(m[s.value]=(m[s.value]||0)+1,m),{});
console.table(sta); // 控制台输出 {A: 教师列数, B: 其它}
alert(`✅ 已完成:每位教师仅 1 个「${diffVal}」,其余全「${defaultVal}」。`);
}else if(++tries<maxRetry){
console.log(`第 ${tries} 次未找到控件,${retryDelay} ms 后重试…`);
setTimeout(runner,retryDelay);
}else{
alert("❌ 多次重试仍未定位到下拉框,请确认页面/iframe 已完全加载或发我 HTML 结构。");
}
}
document.readyState==="complete"
? runner()
: window.addEventListener("load",runner);
})();
2.2 Bookmarklet (Bookmark)
- Create a new item in the browser bookmark bar, name it arbitrarily "One-Click Evaluation".
- Paste the entire javascript: line from the Quick Start paragraph into the URL.
- Enter evaluation page later → Click bookmark → Automatically finished.
Pros: Cross-browser, no extensions; Cons: Still need to click once manually.
2.3 Tampermonkey / Violentmonkey Auto Script
- Install Tampermonkey (Chrome / Edge), Violentmonkey (Firefox), etc.
- Create a new script, paste the full version in; change
@matchto your school's evaluation URL. - Save.
- Visit the webpage later, the script executes automatically, no operation needed.
// ==UserScript==
// @name 评教批量填写
// @match *://pj.yourschool.edu/* // ← 按实际地址修改
// @run-at document-end
// ==/UserScript==
// ……(粘贴完整版主体函数即可)
3. Custom Evaluation Grades
| Requirement | Modification |
|---|---|
| Want to give mostly A, 1 C per column | defaultVal="A"; diffVal="C" |
| Specify the 2nd row is always diffVal | Change set(col[Math.random()*…], diffVal) to set(col[1], diffVal) |
4. Summary
- Positioning:
querySelectorAllgrabs<select> - Grouping: Use
JS\d+ / js\d+column numbers to distinguish teachers - Assignment: Fill all default, then randomize 1 different
- Ensure Uniqueness: Pay attention to secondary validation and deduplication
- Compatibility: Case sensitivity, iframe, event triggering
- Three Usages: One-time paste / Bookmark / Tampermonkey
With this script, end-of-term evaluation no longer requires clicking one by one—one click, done in seconds.
References/Credits
- Universal Naming Convention for Educational Administration Systems (
DataGrid1$ctlXX$JSX) - MDN:
Element.dispatchEventusage - StackOverflow: How to trigger
change&inputevents simultaneously in JS