Discuz! X 下Ajax返回内容无法显示、页面无法跳转的处理方法
分类 网站技术/村民张先生 发布于 2025-11-18 02:00
在部分情况下,例如Chrome 142版本,安装“鼠标手势”扩展后,Discuz登录无跳转(实际已登录成功),管理操作等Ajax浮窗无法显示。可进行如下修改。
1、static/js/common.js 中查找 ajaxpost 函数,将该函数整体替换为以下3个(为现代化浏览器增加xhr方式):
// 旧逻辑:通过隐藏 iframe 提交表单
function ajaxpost_iframe(formid, showid, waitid, showidclass, submitbtn, recall) {
var waitid = typeof waitid == 'undefined' || waitid === null ? showid : (waitid !== '' ? waitid : '');
var showidclass = !showidclass ? '' : showidclass;
var ajaxframeid = 'ajaxframe';
var ajaxframe = $(ajaxframeid);
var curform = $(formid);
var formtarget = curform.target;
var handleResult = function() {
var s = '';
var evaled = false;
showloading('none');
try {
s = $(ajaxframeid).contentWindow.document.XMLDocument.text;
} catch(e) {
try {
s = $(ajaxframeid).contentWindow.document.documentElement.firstChild.wholeText;
} catch(e) {
try {
s = $(ajaxframeid).contentWindow.document.documentElement.firstChild.nodeValue;
} catch(e) {
s = '内部错误,无法显示此内容';
}
}
}
if(s != '' && s.indexOf('ajaxerror') != -1) {
evalscript(s);
evaled = true;
}
if(showidclass) {
if(showidclass != 'onerror') {
$(showid).className = showidclass;
} else {
showError(s);
ajaxerror = true;
}
}
if(submitbtn) {
submitbtn.disabled = false;
}
if(!evaled && (typeof ajaxerror == 'undefined' || !ajaxerror)) {
ajaxinnerhtml($(showid), s);
}
ajaxerror = null;
if(curform) curform.target = formtarget;
if(typeof recall == 'function') {
recall();
} else {
eval(recall);
}
if(!evaled) evalscript(s);
ajaxframe.loading = 0;
if(!BROWSER.firefox || BROWSER.safari) {
$('append_parent').removeChild(ajaxframe.parentNode);
} else {
setTimeout(
function(){
$('append_parent').removeChild(ajaxframe.parentNode);
},
100
);
}
};
if(!ajaxframe) {
var div = document.createElement('div');
div.style.display = 'none';
div.innerHTML = '<iframe name="' + ajaxframeid + '" id="' + ajaxframeid + '" loading="1"></iframe>';
$('append_parent').appendChild(div);
ajaxframe = $(ajaxframeid);
} else if(ajaxframe.loading) {
return false;
}
_attachEvent(ajaxframe, 'load', handleResult);
showloading();
curform.target = ajaxframeid;
var action = curform.getAttribute('action');
action = hostconvert(action);
curform.action = action.replace(/\&inajax\=1/g, '')+'&inajax=1';
curform.submit();
if(submitbtn) {
submitbtn.disabled = true;
}
doane();
return false;
}
// 新逻辑:优先用 XHR 提交表单(需要配合 ajaxpost_iframe 和 ajaxpost 入口)
function ajaxpost_xhr(formid, showid, waitid, showidclass, submitbtn, recall) {
if (!window.XMLHttpRequest) {
return false;
}
var waitid = typeof waitid == 'undefined' || waitid === null ? showid : (waitid !== '' ? waitid : '');
var showidclass = !showidclass ? '' : showidclass;
var curform = $(formid);
if (!curform) {
return false;
}
var action = curform.getAttribute('action');
action = hostconvert(action);
// 与旧逻辑一致:统一拼接 &inajax=1
var url = action.replace(/\&inajax\=1/g, '') + '&inajax=1&ajaxv=2';
var method = (curform.method || 'POST').toUpperCase();
var xhr;
try {
xhr = new XMLHttpRequest();
} catch (e) {
return false;
}
showloading();
if (submitbtn) {
submitbtn.disabled = true;
}
// 使用浏览器内建 API 从表单生成 x-www-form-urlencoded 字符串
var body = '';
try {
var fd = new FormData(curform);
// 仅在 URL 中确实包含 inajax=1 时,过滤掉 loginsubmit 字段
if (url.indexOf('inajax=1') != -1) {
fd.delete('loginsubmit');
}
var usp = new URLSearchParams(fd);
body = usp.toString();
} catch (e) {
body = '';
}
if (method == 'GET') {
// GET 的话,把 body 拼到 URL 上
if (body) {
url += (url.indexOf('?') == -1 ? '?' : '&') + body;
}
}
xhr.open(method, url, true);
xhr.setRequestHeader('X-DZ-AJAX', '2');
// 只有 POST 时需要设置 Content-Type 和发送 body
if (method == 'POST') {
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
}
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
showloading('none');
if (submitbtn) {
submitbtn.disabled = false;
}
if (xhr.status >= 200 && xhr.status < 300) {
var respText = xhr.responseText;
var handled = false;
// 1)JSON 分支(预留:如果未来后端实现了 JSON)
if (respText) {
try {
var data = JSON.parse(respText);
if (data && typeof data === 'object' && data.status) {
handled = true;
if (data.html && showid && $(showid)) {
ajaxinnerhtml($(showid), data.html);
}
if (data.redirect) {
window.location.href = data.redirect;
return;
}
if (data.reload) {
window.location.reload();
return;
}
}
} catch (e) {}
}
// 2)兜底:Discuz 旧式 XML + CDATA(尽量复用旧 ajaxpost_iframe 的行为)
if (!handled) {
var s = respText || '';
var cdataMatch = s.match(/<!\[CDATA\[([\s\S]*)\]\]>/);
if (cdataMatch && cdataMatch[1]) {
s = cdataMatch[1];
}
var evaled = false;
// 与旧逻辑一致:优先处理 ajaxerror 情况
if (s != '' && s.indexOf('ajaxerror') != -1) {
try {
evalscript(s);
evaled = true;
} catch (e) {}
}
if (showidclass) {
if (showidclass != 'onerror') {
if (showid && $(showid)) {
$(showid).className = showidclass;
}
} else {
if (typeof showError == 'function') {
try {
showError(s);
} catch (e) {}
}
window.ajaxerror = true;
}
}
if (!evaled && (typeof ajaxerror == 'undefined' || !ajaxerror)) {
if (showid && $(showid)) {
ajaxinnerhtml($(showid), s);
}
}
window.ajaxerror = null;
if (!evaled) {
try {
evalscript(s);
} catch (e) {}
}
}
// 3)保持原 recall 行为
if (typeof recall == 'function') {
recall();
} else if (typeof recall == 'string' && recall) {
try { eval(recall); } catch (e) {}
}
} else {
// XHR 请求失败时,回退原 iframe 行为
ajaxpost_iframe(formid, showid, waitid, showidclass, submitbtn, recall);
}
}
};
try {
if (method == 'POST') {
xhr.send(body);
} else {
xhr.send(null);
}
} catch (e) {
return false;
}
doane();
return true;
}
// 对外统一入口:优先 XHR,失败再用 iframe
function ajaxpost(formid, showid, waitid, showidclass, submitbtn, recall) {
// 如果 XHR 通路成功受理,则不再走 iframe
if(ajaxpost_xhr(formid, showid, waitid, showidclass, submitbtn, recall)) {
return false;
}
// 否则回退到原来的 iframe 模式
return ajaxpost_iframe(formid, showid, waitid, showidclass, submitbtn, recall);
}
2、由于XHR Ajax提交的内容是UTF-8编码的,如何你的站点是GBK编码,还需要进行转码。
在 source/function/function_core.php 的末尾增加下面的函数:
// 统一兼容 XHR Ajax (ajaxv=2 / X-DZ-AJAX: 2) 在 GBK 站点上的 UTF-8 提交参数
function fix_request_charset() {
// 只对 GBK 站点做兼容
if (defined('CHARSET') && CHARSET != 'gbk') {
return;
}
// 只兼容新版 XHR 通路:
// 1)URL 中明确带 ajaxv=2
// 2)请求头中带 X-DZ-AJAX: 2
$is_new_ajax = false;
if (isset($_GET['ajaxv']) && $_GET['ajaxv'] == '2') {
$is_new_ajax = true;
} elseif (!empty($_SERVER['HTTP_X_DZ_AJAX']) && $_SERVER['HTTP_X_DZ_AJAX'] == '2') {
$is_new_ajax = true;
}
if (!$is_new_ajax) {
// 老的同步请求、老的 iframe Ajax 不动
return;
}
// 内部小工具:判断字符串中是否包含 UTF-8 多字节模式
$has_utf8_multibyte = function($str) {
if (!is_string($str) || $str === '') {
return false;
}
// 匹配常见 UTF-8 多字节序列:2字节、3字节、4字节
return preg_match(
'/[\xC0-\xDF][\x80-\xBF]|' .
'[\xE0-\xEF][\x80-\xBF]{2}|' .
'[\xF0-\xF7][\x80-\xBF]{3}/',
$str
) ? true : false;
};
// 对 $_GET 和 $_POST 做一次 UTF-8 → GBK 的“按需转换”
foreach (array(&$_GET, &$_POST) as &$super) {
foreach ($super as $k => $v) {
if (!is_string($v)) {
continue;
}
if ($v === '') {
continue;
}
// 先判断是否包含 UTF-8 多字节模式,
// 避免对纯数字、纯英文、已是 GBK 的字段做无谓转换
if (!$has_utf8_multibyte($v)) {
continue;
}
// 看起来像 UTF-8,多数情况直接按 UTF-8 → GBK 转
$converted = diconv($v, 'UTF-8', 'GBK');
if ($converted !== '' && $converted !== false) {
$super[$k] = $converted;
}
}
}
}
然后再 source/class/discuz/discuz_application.php 中查找 $this->_init_input(); ,在其下方增加:
if(function_exists('fix_request_charset')) {
fix_request_charset();
}
欢迎谈谈你的看法(无须登录) *正文中请勿包含"http://"否则将被拦截