JavaScript - 如何检测更改URL的源?

时间:2016-12-11 17:53:46

标签: javascript

我只需输入url并按Enter即可阻止用户访问某个页面。例如,用户可以通过一些向导访问此页面。我知道javaScript可以监控浏览器上的点击事件,是否可以监控url更改?换句话说,是否可以知道用户如何访问该页面? (自己添加路径,或者只是按照应用程序向导自然添加?)

编辑:
使用案例:在线测验,包含多个问题和页面,最终是结果页面。我想在回答一个问题后阻止用户进入结果页面。

1 个答案:

答案 0 :(得分:2)

由于您的问题确实没有启动代码,因此很难知道您当前使用的是哪个框架。因此,我实施了一个非常基本的测验(它没有真正做到答案部分,没有显示任何结果,而且总体来说非常基本,但我想提供一个有点完整的例子。)

要检测页面内的更改(当前哈希路径),您可以订阅窗口的hashchange事件,例如:

window.addEventListener('hashchange', handleRoute.bind(router));

但是,为了确保用户不直接导航到特定路线,您还可以订阅load事件。

window.addEventListener('load', handleRoute.bind(router));

要验证(在我的情况下)是否允许用户在当前路由上,我添加了一个验证方法,只需检查:

  • 如有疑问:当前问题是下一个要回答的问题
  • 如果结果:确保我已收到所有答案

这部分在此处理:

function isPageValid(route, qry) {
  switch (route.scope) {
    case 'questions':
      // only allow the question page when the current question is the actual one in the querystring
      return (parseInt(qry.split('=')[1]) === answered.length) && answered.length <= questions.length;
      // only allow the results when we have all the answers
    case 'results':
      return answered.length === parseInt(questions.length);
  }
  // everything else should be valid (eg: default page)
  return true;
}

由于无论如何根据我的路由器加载当前路由,我不必担心其他页面当时有效,所以如果它不是问题或结果,我总是发送{{1} } back:)

如果不存在,将显示默认页面

对于其余部分,小提琴是非常基本的,它向您显示起始页面,2个潜在的问题而您无法真正选择任何内容,但是在问题列表完成时仅显示结果页面的原则应该是清楚的。

我在代码中添加了一些注释,希望一切都清楚:)

true
'use strict';
var questions = [{
    title: 'Is SO a good resource',
    answers: ["yes", "no", "sometimes"],
    correct: 2
  }, {
    title: 'Do you like to use stackoverflow on a daily basis',
    answers: ["yes", "no"],
    correct: 0
  }],
  answered = [];

// bind the nextQuestion element to the nextQuestion function
document.getElementById('nextQuestion').addEventListener('click', nextQuestion);
/*
 * @method nextQuestion
 * here the user will click when he wants to navigate to the next question
 * If all questions are completed, it will navigate to the results page
 * If there are more questions, it will navigate to /q=currentQuestion+1
 */
function nextQuestion(e) {
  answered.push({
    value: 0
  });
  if (answered.length < questions.length) {
    document.location.href = '#/q=' + answered.length;
  } else {
    document.location.href = '#/results';
  }
  e.preventDefault();
}

/*
 * @method showQuestion
 * Gets called when the question gets changed
 */
function showQuestion(route, qry) {
  var currentQuestion = answered.length;
  document.getElementById('currentQuestion').innerHTML = currentQuestion;
  document.getElementById('question').innerHTML = questions[currentQuestion].title;
  if (currentQuestion === questions.length - 1) {
    document.getElementById('nextQuestion').innerHTML = 'show results';
  } else {
    document.getElementById('nextQuestion').innerHTML = 'next question';
  }
}

/*
 * @method showResults
 * Dummy method, answered are should contain all current answers
 * just prints a message to the console
 */
function showResults(route, qry) {
  console.log('can finally see the results :)');
}

/* 
 * @method isPageValid
 * Validates the current route & qry
 * @param route the current active route
 * @param qry the current querystring that was validated to get to this route
 * @returns true if the page is valid, false if it is not valid
 */
function isPageValid(route, qry) {
  switch (route.scope) {
    case 'questions':
      // only allow the question page when the current question is the actual one in the querystring
      return (parseInt(qry.split('=')[1]) === answered.length) && answered.length <= questions.length;
      // only allow the results when we have all the answers
    case 'results':
      return answered.length === parseInt(questions.length);
  }
  // everything else should be valid (eg: default page)
  return true;
}

/*
 * @method handleRoute
 * All routes are part of it's context (this object)
 * Loops all properties of the context and checks which route matches the current part after #
 */
function handleRoute() {
  var qry = document.location.href.split('#')[1],
    result, defaultRoute, prop;
  // hide all .scoped-block elements
  document.querySelectorAll('.scoped-block').forEach(item => {
    item.classList.add('hidden');
  });
  console.log('current query: ' + qry);
  for (prop in this) {
    if (this.hasOwnProperty(prop)) {
      if (!this[prop].test) {
        defaultRoute = this[prop];
      } else {
        if (this[prop].test.test(qry)) {
          result = this[prop];
          console.log('matches: ' + prop);
        }
      }
    }
  }
  // if we have a designated page, validate it
  if (result && !result.validate(result, qry)) {
    // the page is not allowed to be shown (route to the default page)
    console.info('cannot navigate to ' + result.scope + ' page (yet)')
    result = undefined;
  }
  // make sure result contains the current valid route or the defaultRoute
  result = result || defaultRoute;
  console.log('current scope: ' + result.scope);

  // set the current scope as the visible element
  document.getElementById(result.scope).classList.remove('hidden');
  if (result.action) {
    result.action(result, qry);
  }
}

// setup the available routes + potential validations
var router = {
  questionPage: {
    test: /\/q=[0-9]{1,2}$/,
    validate: isPageValid,
    action: showQuestion,
    scope: 'questions'
  },
  resultPage: {
    test: /\/results$/,
    validate: isPageValid,
    action: showResults,
    scope: 'results'

  },
  startPage: {
    action: function() {
      // reset the answers
      answered.splice(0, answered.length);
    },
    scope: 'startPage'
  }
};

// adds the handle route method to the onload + hashchange method
window.addEventListener('hashchange', handleRoute.bind(router));
window.addEventListener('load', handleRoute.bind(router));
.hidden { display: none; visibility: hidden; }
.scoped-block {
  // dummy element
}