Я загрузил скрипт smoothscroll.js в tampermonkey. IDE tampermonkey выявила множество ошибок для этого сценария, но, тем не менее, сценарий работал отлично. Однако я решил исправить эти ошибки, что было очень просто (отсутствие точек с запятой, отсутствие () и некоторые другие проблемы).
Однако я не могу исправить следующую ошибку. Есть цикл do-while с присваиванием как условным выражением.
function overflowingAncestor(el) {
var elems = [];
var rootScrollHeight = root.scrollHeight;
do {
var cached = cache[uniqueID(el)];
if (cached) {
return setCache(elems, cached);
}
elems.push(el);
if (rootScrollHeight === el.scrollHeight) {
if (!isFrame || root.clientHeight + 10 < rootScrollHeight) {
return setCache(elems, document.body);
}
} else if (el.clientHeight + 10 < el.scrollHeight) {
overflow = getComputedStyle(el, "").getPropertyValue("overflow-y");
if (overflow === "scroll" || overflow === "auto") {
return setCache(elems, el);
}
}
} while (el = el.parentNode); //<--- Error
}Я думал, что исправление будет заключаться в простой замене while (el = el.parentNode); на while (el == el.parentNode);, но плавная прокрутка больше не работает после применения этого.
Я знаю, что могу оставить все как есть, но я пытаюсь понять, почему возникает ошибка, даже если она работает?
Если вы хотите опробовать его вживую, перейдите к эта страница и загрузите следующий скрипт в свой движок tampermonkey.
// ==UserScript==
// @name Test
// @namespace http://bs.to/
// @version 2.2
// @description Test
// @author Me
// @match https://bs.to/andere-serien
// @require https://code.jquery.com/jquery-3.3.1.min.js
// ==/UserScript==
//Smoothscroll.js
(function() {
var defaultOptions = {
frameRate: 300,
animationTime: 1500,
stepSize: 120,
pulseAlgorithm: true,
pulseScale: 8,
pulseNormalize: 1,
accelerationDelta: 20,
accelerationMax: 1,
keyboardSupport: true,
arrowScroll: 50,
touchpadSupport: true,
fixedBackground: true,
excluded: ""
};
var options = defaultOptions;
var isExcluded = false;
var isFrame = false;
var direction = {
x: 0,
y: 0
};
var initDone = false;
var root = document.documentElement;
var activeElement;
var observer;
var deltaBuffer = [120, 120, 120];
var key = {
left: 37,
up: 38,
right: 39,
down: 40,
spacebar: 32,
pageup: 33,
pagedown: 34,
end: 35,
home: 36
};
options = defaultOptions;
function initTest() {
var disableKeyboard = false;
if (disableKeyboard) {
removeEvent("keydown", keydown);
}
if (options.keyboardSupport && !disableKeyboard) {
addEvent("keydown", keydown);
}
}
function init() {
if (!document.body)
return;
var body = document.body;
var html = document.documentElement;
var windowHeight = window.innerHeight;
var scrollHeight = body.scrollHeight;
root = (document.compatMode.indexOf('CSS') >= 0) ? html : body;
activeElement = body;
initTest();
initDone = true;
if (top != self) {
isFrame = true;
} else if (scrollHeight > windowHeight && (body.offsetHeight <= windowHeight || html.offsetHeight <= windowHeight)) {
var pending = false;
var refresh = function() {
if (!pending && html.scrollHeight != document.height) {
pending = true;
setTimeout(function() {
html.style.height = document.height + 'px';
pending = false;
}, 500);
}
};
html.style.height = 'auto';
setTimeout(refresh, 10);
if (root.offsetHeight <= windowHeight) {
var underlay = document.createElement("div");
underlay.style.clear = "both";
body.appendChild(underlay);
}
}
if (!options.fixedBackground && !isExcluded) {
body.style.backgroundAttachment = "scroll";
html.style.backgroundAttachment = "scroll";
}
}
var que = [];
var pending = false;
var lastScroll = +new Date();
function scrollArray(elem, left, top, delay) {
if (delay === undefined) {
delay = 1000;
}
directionCheck(left, top);
if (options.accelerationMax != 1) {
var now = +new Date();
var elapsed = now - lastScroll;
if (elapsed < options.accelerationDelta) {
var factor = (1 + (30 / elapsed)) / 2;
if (factor > 1) {
factor = Math.min(factor, options.accelerationMax);
left *= factor;
top *= factor;
}
}
lastScroll = +new Date();
}
que.push({
x: left,
y: top,
lastX: (left < 0) ? 0.99 : -0.99,
lastY: (top < 0) ? 0.99 : -0.99,
start: +new Date()
});
if (pending) {
return;
}
var scrollWindow = (elem === document.body);
var step = function(time) {
var now = +new Date();
var scrollX = 0;
var scrollY = 0;
for (var i = 0; i < que.length; i++) {
var item = que[i];
var elapsed = now - item.start;
var finished = (elapsed >= options.animationTime);
var position = (finished) ? 1 : elapsed / options.animationTime;
if (options.pulseAlgorithm) {
position = pulse(position);
}
var x = (item.x * position - item.lastX) >> 0;
var y = (item.y * position - item.lastY) >> 0;
scrollX += x;
scrollY += y;
item.lastX += x;
item.lastY += y;
if (finished) {
que.splice(i, 1);
i--;
}
}
if (scrollWindow) {
window.scrollBy(scrollX, scrollY);
} else {
if (scrollX)
elem.scrollLeft += scrollX;
if (scrollY)
elem.scrollTop += scrollY;
}
if (!left && !top) {
que = [];
}
if (que.length) {
requestFrame(step, elem, (delay / options.frameRate + 1));
} else {
pending = false;
}
};
requestFrame(step, elem, 0);
pending = true;
}
function wheel(event) {
if (!initDone) {
init();
}
var target = event.target;
var overflowing = overflowingAncestor(target);
if (!overflowing || event.defaultPrevented || isNodeName(activeElement, "embed") || (isNodeName(target, "embed") && /\.pdf/i.test(target.src))) {
return true;
}
var deltaX = event.wheelDeltaX || 0;
var deltaY = event.wheelDeltaY || 0;
if (!deltaX && !deltaY) {
deltaY = event.wheelDelta || 0;
}
if (!options.touchpadSupport && isTouchpad(deltaY)) {
return true;
}
if (Math.abs(deltaX) > 1.2) {
deltaX *= options.stepSize / 120;
}
if (Math.abs(deltaY) > 1.2) {
deltaY *= options.stepSize / 120;
}
scrollArray(overflowing, -deltaX, -deltaY);
event.preventDefault();
}
function keydown(event) {
var target = event.target;
var modifier = event.ctrlKey || event.altKey || event.metaKey || (event.shiftKey && event.keyCode !== key.spacebar);
if (/input|textarea|select|embed/i.test(target.nodeName) || target.isContentEditable || event.defaultPrevented || modifier) {
return true;
}
if (isNodeName(target, "button") && event.keyCode === key.spacebar) {
return true;
}
var shift, x = 0,
y = 0;
var elem = overflowingAncestor(activeElement);
var clientHeight = elem.clientHeight;
if (elem == document.body) {
clientHeight = window.innerHeight;
}
switch (event.keyCode) {
case key.up:
y = -options.arrowScroll;
break;
case key.down:
y = options.arrowScroll;
break;
case key.spacebar:
shift = event.shiftKey ? 1 : -1;
y = -shift * clientHeight * 0.9;
break;
case key.pageup:
y = -clientHeight * 0.9;
break;
case key.pagedown:
y = clientHeight * 0.9;
break;
case key.home:
y = -elem.scrollTop;
break;
case key.end:
var damt = elem.scrollHeight - elem.scrollTop - clientHeight;
y = (damt > 0) ? damt + 10 : 0;
break;
case key.left:
x = -options.arrowScroll;
break;
case key.right:
x = options.arrowScroll;
break;
default:
return true;
}
scrollArray(elem, x, y);
event.preventDefault();
}
function mousedown(event) {
activeElement = event.target;
}
var cache = {};
setInterval(function() {
cache = {};
}, 10 * 1000);
var uniqueID = (function() {
var i = 0;
return function(el) {
return el.uniqueID || (el.uniqueID = i++);
};
})();
function setCache(elems, overflowing) {
for (var i = elems.length; i--;)
cache[uniqueID(elems[i])] = overflowing;
return overflowing;
}
function overflowingAncestor(el) {
var elems = [];
var rootScrollHeight = root.scrollHeight;
do {
var cached = cache[uniqueID(el)];
if (cached) {
return setCache(elems, cached);
}
elems.push(el);
if (rootScrollHeight === el.scrollHeight) {
if (!isFrame || root.clientHeight + 10 < rootScrollHeight) {
return setCache(elems, document.body);
}
} else if (el.clientHeight + 10 < el.scrollHeight) {
overflow = getComputedStyle(el, "").getPropertyValue("overflow-y");
if (overflow === "scroll" || overflow === "auto") {
return setCache(elems, el);
}
}
} while (el = el.parentNode);
}
function addEvent(type, fn, bubble) {
window.addEventListener(type, fn, (bubble || false));
}
function removeEvent(type, fn, bubble) {
window.removeEventListener(type, fn, (bubble || false));
}
function isNodeName(el, tag) {
return (el.nodeName || "").toLowerCase() === tag.toLowerCase();
}
function directionCheck(x, y) {
x = (x > 0) ? 1 : -1;
y = (y > 0) ? 1 : -1;
if (direction.x !== x || direction.y !== y) {
direction.x = x;
direction.y = y;
que = [];
lastScroll = 0;
}
}
var deltaBufferTimer;
function isTouchpad(deltaY) {
if (!deltaY)
return;
deltaY = Math.abs(deltaY);
deltaBuffer.push(deltaY);
deltaBuffer.shift();
clearTimeout(deltaBufferTimer);
var allDivisable = (isDivisible(deltaBuffer[0], 120) && isDivisible(deltaBuffer[1], 120) && isDivisible(deltaBuffer[2], 120));
return !allDivisable;
}
function isDivisible(n, divisor) {
return (Math.floor(n / divisor) == n / divisor);
}
var requestFrame = (function() {
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || function(callback, element, delay) {
window.setTimeout(callback, delay || (1000 / 60));
};
})();
function pulse_(x) {
var val, start, expx;
x = x * options.pulseScale;
if (x < 1) {
val = x - (1 - Math.exp(-x));
} else {
start = Math.exp(-1);
x -= 1;
expx = 1 - Math.exp(-x);
val = start + (expx * (1 - start));
}
return val * options.pulseNormalize;
}
function pulse(x) {
if (x >= 1)
return 1;
if (x <= 0)
return 0;
if (options.pulseNormalize == 1) {
options.pulseNormalize /= pulse_(1);
}
return pulse_(x);
}
var isChrome = /chrome/i.test(window.navigator.userAgent);
var wheelEvent = null;
if ("onwheel" in document.createElement("div"))
wheelEvent = "wheel";
else if ("onmousewheel" in document.createElement("div"))
wheelEvent = "mousewheel";
if (wheelEvent && isChrome) {
addEvent(wheelEvent, wheel);
addEvent("mousedown", mousedown);
addEvent("load", init);
}
})();@Xufox, который предназначен только для линтера, но не меняет выражения.
Error ... на самом деле нет warning ... но чтобы линтер был доволен while ((el = el.parentNode))@Xufox, потому что он говорит, что ожидает условное выражение вместо присваивания. Я думал, что смогу исправить это, заменив его на один с помощью ==.
@Black - это изменит логику
@JaromandaX, а почему тогда вообще ошибка отображается?
потому что а) это нет ошибка; и б) уже указанные причины
@Black Это предупреждение линтера. В большинстве случаев вы хотите сравнивать в условиях цикла, а не назначать, и сбивание с толку = и == является распространенной ошибкой новичков, поэтому линтеры предполагают, что назначения неверны в условиях цикла. Даже люди, которые читают ваш код, могут запутаться, увидев назначение в условии цикла, поэтому лучше для начала более явным образом изложить свои намерения.
Но задание служит важной цели, поэтому вы не можете просто заменить его. Помимо уже предложенных решений, вы можете решить эту проблему, переместив выражение присваивания в последнюю строку блока while, а затем сделав такое условие: while (el);
@MikaelLennholm - как это "кроме уже предложенных решений" - это именно мое решение: p
@JaromandaX Я не видел вашего ответа, я имел в виду только то, что было упомянуто в комментариях
@MikaelLennholm - Я поставил: p после своего комментария - все хорошо: D



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Я не знаком с tampermonkey, но из того, что вы описываете, кажется, что он строже, чем парсер JavaScript. В любом случае, чтобы понять, почему работает while (el = el.parentNode), вам нужно знать две вещи.
(1) Условия в JavaScript (используемые в if, while и втором выражении обычного цикла for) не обязательно должны быть строго логическими выражениями, такими как x == 1. Это может быть любое выражение, которое вам нравится. JavaScript оценит это выражение, а затем решит, является ли результат «правдивым» или «ложным» (это настоящие слова). Значение «ложь» - это false, null, undefined, 0 или пустая строка. Любое другое значение является «правдивым» (включая пустой массив или пустой объект). Истинные значения означают, что ветвь if будет выполнена, или, в вашем случае, цикл будет продолжен.
(2) Назначение значений в JavaScript - это такое же выражение, как и любое другое, и выражения всегда имеют результат. В случае присвоения значения это значение, которое было присвоено. Тот факт, что переменная слева в конечном итоге содержит присвоенное значение, является, строго говоря, «побочным эффектом».
Учитывая эти два факта, вот что происходит в while (el = el.parentNode):
el.parentNode. Это может быть узел или null, если el не имеет родителя (т.е. если это сам документ).el.parentNode присвоено el. Результат этой операции присвоенного значения.el не было родителя, el.parentNode был null, что является ложным значением, поэтому цикл останавливается. Если это не так, цикл продолжается.Итак, JavaScript позволяет присваивать переменные в условиях, но, похоже, tampermonkey этого не делает. Вероятно, это связано с тем, что в 99% случаев вы действительно хотите использовать == в состоянии, и очень легко случайно набрать = вместо ==. Это может привести к большому разочарованию и возникновению труднодоступных ошибок, поскольку, как обсуждалось выше, JavaScript с радостью обработает его без ошибок, поэтому многие линтеры отметят это как ошибку.
Кроме того, el == el.parentNode не имеет смысла, потому что узел никогда не может быть равен своему родителю.
Чтобы линтер порадовал, переместите назначение в строку перед while, а затем в while(el)
do {
// snip
el = el.parentNode;
} while (el);
в качестве альтернативы
do {
// snip
} while ((el = el.parentNode));
не уверен, что все линтеры с этим справятся, но обычно jshint
Почему замена
=на==может исправить это? Вам делать нужно назначение здесь. Идиоматически это можно исправить, используя двойной набор круглых скобок:while((el = el.parentNode)).