Карточка: обновление в DOM на месте (без перезагрузки) — страница не дёргается; бейджи не переносятся
Вместо location.reload при смене подписи карточка теперь обновляется рекурсивным морфингом
<main>: догружаем ту же страницу и сливаем только изменившиеся узлы, одинаковые поддеревья не
трогаем. Прокрутка окна и боковых блоков («Путь заявки» .claim-tl / .in-tl) не сбивается,
выделение в неизменном тексте сохраняется, нет прыжков и мигания. Морфинг пропускается, если
правится форма (dirty) или идёт выделение. Копи-кнопки переинициализируются (bjInitCopyBars).
Плюс .badge { white-space:nowrap } — в журнале стрелка «↗ Исходящий» больше не переносится над словом.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -161,7 +161,7 @@ th { color:var(--muted); font-weight:600; font-size:11px; text-transform:upperca
|
|||||||
tbody tr:hover td { background:var(--accent-weak); }
|
tbody tr:hover td { background:var(--accent-weak); }
|
||||||
|
|
||||||
/* ---- Бейджи статусов ---- */
|
/* ---- Бейджи статусов ---- */
|
||||||
.badge { display:inline-block; padding:3px 9px; border-radius:20px; font-size:11px; font-weight:600; }
|
.badge { display:inline-block; padding:3px 9px; border-radius:20px; font-size:11px; font-weight:600; white-space:nowrap; }
|
||||||
.live { font-size:11.5px; margin-top:4px; color:var(--muted); display:flex; align-items:center; gap:6px; line-height:1.3 }
|
.live { font-size:11.5px; margin-top:4px; color:var(--muted); display:flex; align-items:center; gap:6px; line-height:1.3 }
|
||||||
.live::before { content:''; width:7px; height:7px; border-radius:50%; background:var(--muted); flex:none }
|
.live::before { content:''; width:7px; height:7px; border-radius:50%; background:var(--muted); flex:none }
|
||||||
.live-wait::before { background:var(--accent); animation:pulse 2.4s var(--ease) infinite }
|
.live-wait::before { background:var(--accent); animation:pulse 2.4s var(--ease) infinite }
|
||||||
@@ -798,7 +798,8 @@ button.dq-link, .dq-link { background:none !important; border:0 !important; padd
|
|||||||
return Promise.resolve(fallback());
|
return Promise.resolve(fallback());
|
||||||
};
|
};
|
||||||
// Кнопка «Скопировать» над каждым блоком скрипта/кода (pre.copyable): копирует текст целиком.
|
// Кнопка «Скопировать» над каждым блоком скрипта/кода (pre.copyable): копирует текст целиком.
|
||||||
(function(){
|
// Вынесено в window.bjInitCopyBars, чтобы вызывать повторно после обновления карточки в DOM.
|
||||||
|
window.bjInitCopyBars = function(){
|
||||||
document.querySelectorAll('pre.copyable').forEach(function(pre){
|
document.querySelectorAll('pre.copyable').forEach(function(pre){
|
||||||
if (pre.previousElementSibling && pre.previousElementSibling.classList.contains('copy-bar')) return;
|
if (pre.previousElementSibling && pre.previousElementSibling.classList.contains('copy-bar')) return;
|
||||||
var bar=document.createElement('div'); bar.className='copy-bar';
|
var bar=document.createElement('div'); bar.className='copy-bar';
|
||||||
@@ -812,7 +813,8 @@ button.dq-link, .dq-link { background:none !important; border:0 !important; padd
|
|||||||
});
|
});
|
||||||
bar.appendChild(b); pre.parentNode.insertBefore(bar, pre);
|
bar.appendChild(b); pre.parentNode.insertBefore(bar, pre);
|
||||||
});
|
});
|
||||||
})();
|
};
|
||||||
|
bjInitCopyBars();
|
||||||
// Цепочка связи с НРД в шапке: обновляется каждые 30 с, чтобы открытая страница не показывала старое.
|
// Цепочка связи с НРД в шапке: обновляется каждые 30 с, чтобы открытая страница не показывала старое.
|
||||||
(function(){
|
(function(){
|
||||||
var el = document.getElementById('nsd-chain');
|
var el = document.getElementById('nsd-chain');
|
||||||
@@ -856,6 +858,48 @@ button.dq-link, .dq-link { background:none !important; border:0 !important; padd
|
|||||||
} catch(e){}
|
} catch(e){}
|
||||||
location.reload();
|
location.reload();
|
||||||
}
|
}
|
||||||
|
// Обновление карточки заявки БЕЗ перезагрузки: догружаем ту же страницу и аккуратно
|
||||||
|
// сливаем изменения в DOM (рекурсивный морфинг <main>). Одинаковые поддеревья не трогаем —
|
||||||
|
// страница не дёргается, прокрутка окна и боковых блоков («Путь заявки») не сбивается,
|
||||||
|
// выделение в неизменном тексте сохраняется. Скролл прокручиваемых блоков восстанавливается.
|
||||||
|
function bjMorphKids(oldP, newP){
|
||||||
|
if (!oldP || !newP) return;
|
||||||
|
var top = oldP.scrollTop, left = oldP.scrollLeft;
|
||||||
|
var olds = [].slice.call(oldP.childNodes), news = [].slice.call(newP.childNodes);
|
||||||
|
var common = Math.min(olds.length, news.length), i;
|
||||||
|
for (i = 0; i < common; i++) bjMorphNode(oldP, olds[i], news[i]);
|
||||||
|
for (i = olds.length - 1; i >= common; i--) oldP.removeChild(olds[i]);
|
||||||
|
for (i = common; i < news.length; i++) oldP.appendChild(news[i].cloneNode(true));
|
||||||
|
oldP.scrollTop = top; oldP.scrollLeft = left;
|
||||||
|
}
|
||||||
|
function bjMorphNode(parent, o, n){
|
||||||
|
if (o.nodeType === 3 || o.nodeType === 8) { // текст/комментарий
|
||||||
|
if (n.nodeType === o.nodeType) { if (o.nodeValue !== n.nodeValue) o.nodeValue = n.nodeValue; }
|
||||||
|
else parent.replaceChild(n.cloneNode(true), o);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (o.nodeType !== 1 || n.nodeType !== 1 || o.nodeName !== n.nodeName) { parent.replaceChild(n.cloneNode(true), o); return; }
|
||||||
|
if (o.outerHTML === n.outerHTML) return; // поддерево не изменилось — не трогаем (скролл/выделение целы)
|
||||||
|
var oa = o.attributes, na = n.attributes, j;
|
||||||
|
for (j = oa.length - 1; j >= 0; j--) { if (!n.hasAttribute(oa[j].name)) o.removeAttribute(oa[j].name); }
|
||||||
|
for (j = 0; j < na.length; j++) { if (o.getAttribute(na[j].name) !== na[j].value) o.setAttribute(na[j].name, na[j].value); }
|
||||||
|
bjMorphKids(o, n); // рекурсивно внутрь
|
||||||
|
}
|
||||||
|
var bjRefreshing = false;
|
||||||
|
function bjRefreshClaim(){
|
||||||
|
if (bjRefreshing) return;
|
||||||
|
bjRefreshing = true;
|
||||||
|
fetch(location.href, {cache: 'no-store', credentials: 'same-origin'})
|
||||||
|
.then(function(r){ return r.ok ? r.text() : Promise.reject(); })
|
||||||
|
.then(function(html){
|
||||||
|
if (dirty || (window.bjSelecting && window.bjSelecting())) return; // правит форму или выделяет — не трогаем
|
||||||
|
var doc = new DOMParser().parseFromString(html, 'text/html');
|
||||||
|
bjMorphKids(document.querySelector('main'), doc.querySelector('main'));
|
||||||
|
if (window.bjInitCopyBars) window.bjInitCopyBars();
|
||||||
|
})
|
||||||
|
.catch(function(){})
|
||||||
|
.then(function(){ bjRefreshing = false; });
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
var raw = sessionStorage.getItem(scrollKey);
|
var raw = sessionStorage.getItem(scrollKey);
|
||||||
if (raw !== null) {
|
if (raw !== null) {
|
||||||
@@ -895,7 +939,7 @@ button.dq-link, .dq-link { background:none !important; border:0 !important; padd
|
|||||||
if (!st) return;
|
if (!st) return;
|
||||||
var big = e.classList.contains('live-big'), was = e.getAttribute('data-sig');
|
var big = e.classList.contains('live-big'), was = e.getAttribute('data-sig');
|
||||||
if (big && was && st.sig && st.sig !== was) {
|
if (big && was && st.sig && st.sig !== was) {
|
||||||
if (!dirty) { bjReloadKeepScroll(); return; }
|
if (!dirty) { bjRefreshClaim(); e.setAttribute('data-sig', st.sig); return; }
|
||||||
meta(e, 'Заявка изменилась. <a href="' + location.pathname + location.search + '">Обновить страницу</a> (отмеченное в форме не сохранится)', 'changed');
|
meta(e, 'Заявка изменилась. <a href="' + location.pathname + location.search + '">Обновить страницу</a> (отмеченное в форме не сохранится)', 'changed');
|
||||||
e.setAttribute('data-sig', st.sig);
|
e.setAttribute('data-sig', st.sig);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user