Я получаю сообщение «Ожидалось условное выражение, а вместо этого увидел задание», хотя оно работает

Я загрузил скрипт 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);
  }
})();

Почему замена = на == может исправить это? Вам делать нужно назначение здесь. Идиоматически это можно исправить, используя двойной набор круглых скобок: while((el = el.parentNode)).

Sebastian Simon 04.04.2018 11:24

@Xufox, который предназначен только для линтера, но не меняет выражения.

Nina Scholz 04.04.2018 11:28
Error ... на самом деле нет warning ... но чтобы линтер был доволен while ((el = el.parentNode))
Jaromanda X 04.04.2018 11:29

@Xufox, потому что он говорит, что ожидает условное выражение вместо присваивания. Я думал, что смогу исправить это, заменив его на один с помощью ==.

Black 04.04.2018 11:30

@Black - это изменит логику

Jaromanda X 04.04.2018 11:30

@JaromandaX, а почему тогда вообще ошибка отображается?

Black 04.04.2018 11:31

потому что а) это нет ошибка; и б) уже указанные причины

Jaromanda X 04.04.2018 11:33

@Black Это предупреждение линтера. В большинстве случаев вы хотите сравнивать в условиях цикла, а не назначать, и сбивание с толку = и == является распространенной ошибкой новичков, поэтому линтеры предполагают, что назначения неверны в условиях цикла. Даже люди, которые читают ваш код, могут запутаться, увидев назначение в условии цикла, поэтому лучше для начала более явным образом изложить свои намерения.

Sebastian Simon 04.04.2018 11:34

Но задание служит важной цели, поэтому вы не можете просто заменить его. Помимо уже предложенных решений, вы можете решить эту проблему, переместив выражение присваивания в последнюю строку блока while, а затем сделав такое условие: while (el);

Lennholm 04.04.2018 11:37

@MikaelLennholm - как это "кроме уже предложенных решений" - это именно мое решение: p

Jaromanda X 04.04.2018 11:37

@JaromandaX Я не видел вашего ответа, я имел в виду только то, что было упомянуто в комментариях

Lennholm 04.04.2018 11:49

@MikaelLennholm - Я поставил: p после своего комментария - все хорошо: D

Jaromanda X 04.04.2018 11:50
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
12
604
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Я не знаком с 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):

  1. Парсер оценивает el.parentNode. Это может быть узел или null, если el не имеет родителя (т.е. если это сам документ).
  2. Значение el.parentNode присвоено el. Результат этой операции присвоенного значения.
  3. Затем этот результат оценивается как правдивый или ложный. Если у 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

Другие вопросы по теме