创建一个Javascript投票系统

时间:2019-03-21 04:17:21

标签: javascript html css

这是我对投票系统的尝试。只有一个时,它会按预期工作。但是,当我尝试将投票系统的数量扩展到一个以上时,它无法按预期工作。问题在于投票系统依赖于upVotedownVote状态。因此,当我增加投票系统的数量时,它们都依赖于相同的两个变量,而不是每个变量都有自己的upVotedownVote变量。我认为有两种方法可以解决我的问题。

  1. 创建一个数组,以便每个投票系统都有自己的upVotedownVote变量。

  2. 使用闭包。

我认为第一个太麻烦且不切实际,我不知道如何实现此解决方案。我尝试了第二个,但是无法正常工作。

我想不出任何简单的方法。我不一定要寻找使用闭包的解决方案。任何简单的原始Javascript 解决方案都可以。

工作代码:

const up_vote_span = document.querySelector('.up-vote');
const down_vote_span = document.querySelector('.down-vote');
let count = document.querySelector('.number');

let upVote = false;
let downVote = false;

up_vote_span.addEventListener('click', function() {

  if (downVote === true) {

    up_vote_span.style.color = "#3CBC8D";
    down_vote_span.style.color = "dimgray";
    count.innerHTML = parseInt(count.innerHTML) + 2;
    upVote = true;
    downVote = false;

  } else if (upVote === false) {
    up_vote_span.style.color = "#3CBC8D";
    count.innerHTML = parseInt(count.innerHTML) + 1;
    upVote = true;
  } else if (upVote === true) {
    up_vote_span.style.color = "dimgray";
    count.innerHTML = parseInt(count.innerHTML) - 1;
    upVote = false;
  }
});

down_vote_span.addEventListener('click', function() {

  if (upVote === true) {
    up_vote_span.style.color = "dimgray";
    down_vote_span.style.color = "#3CBC8D";
    count.innerHTML = parseInt(count.innerHTML) - 2;
    downVote = true;
    upVote = false;
  } else if (downVote === false) {
    down_vote_span.style.color = "#3CBC8D";
    count.innerHTML = parseInt(count.innerHTML) - 1;
    downVote = true;
  } else if (downVote === true) {
    down_vote_span.style.color = "dimgray";
    count.innerHTML = parseInt(count.innerHTML) + 1;
    downVote = false;
  }
});
.number {
  display: inline-block;
  text-align: center;
}

.vote {
  display: flex;
  flex-direction: column;
  text-align: center;
}

.up-vote {
  color: dimgray;
  cursor: pointer;
}

.down-vote {
  cursor: pointer;
  color: dimgray;
}
<!DOCTYPE html>
<html lang="en" dir="ltr">

<head>
  <meta charset="utf-8">
  <title></title>
  <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css" integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/" crossorigin="anonymous">
</head>


  <div class="vote">
    <span class="up-vote"><i class="fas fa-angle-up"></i></span>
    <span class="number">990</span>
    <span class="down-vote"><i class="fas fa-angle-down"></i></span>
  </div>

有问题的代码:

const up_vote_span = document.getElementsByClassName('up-vote');
const down_vote_span = document.getElementsByClassName('down-vote');
const count = document.getElementsByClassName('number');

let upVote = false;
let downVote = false;

for (let i = 0; i < count.length; i++) {
  up_vote_span[i].addEventListener('click', function() {

    if (downVote === true) {
      up_vote_span[i].style.color = "#3CBC8D";
      down_vote_span[i].style.color = "dimgray";
      count[i].innerHTML = parseInt(count[i].innerHTML) + 2;
      upVote = true;
      downVote = false;

    } else if (upVote === false) {
      up_vote_span[i].style.color = "#3CBC8D";
      count[i].innerHTML = parseInt(count[i].innerHTML) + 1;
      upVote = true;
    } else if (upVote === true) {
      up_vote_span[i].style.color = "dimgray";
      count[i].innerHTML = parseInt(count[i].innerHTML) - 1;
      upVote = false;
    }
  });

  down_vote_span[i].addEventListener('click', function() {

    if (upVote === true) {
      up_vote_span[i].style.color = "dimgray";
      down_vote_span[i].style.color = "#3CBC8D";
      count[i].innerHTML = parseInt(count[i].innerHTML) - 2;
      downVote = true;
      upVote = false;
    } else if (downVote === false) {
      down_vote_span[i].style.color = "#3CBC8D";
      count[i].innerHTML = parseInt(count[i].innerHTML) - 1;
      downVote = true;
    } else if (downVote === true) {
      down_vote_span[i].style.color = "dimgray";
      count[i].innerHTML = parseInt(count[i].innerHTML) + 1;
      downVote = false;
    }
  });
}
.number {
  display: inline-block;
  text-align: center;
}

.vote {
  display: flex;
  flex-direction: column;
  text-align: center;
}

.up-vote {
  color: dimgray;
  cursor: pointer;
}

.down-vote {
  cursor: pointer;
  color: dimgray;
}
<!DOCTYPE html>
<html lang="en" dir="ltr">

<head>
  <meta charset="utf-8">
  <title></title>
  <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css" integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/" crossorigin="anonymous">
</head>

<body>

  <div class="vote">
    <span class="up-vote"><i class="fas fa-angle-up"></i></span>
    <span class="number">990</span>
    <span class="down-vote"><i class="fas fa-angle-down"></i></span>
  </div>
  <div class="vote">
    <span class="up-vote"><i class="fas fa-angle-up"></i></span>
    <span class="number">990</span>
    <span class="down-vote"><i class="fas fa-angle-down"></i></span>
  </div>
  <div class="vote">
    <span class="up-vote"><i class="fas fa-angle-up"></i></span>
    <span class="number">990</span>
    <span class="down-vote"><i class="fas fa-angle-down"></i></span>
  </div>
  <div class="vote">
    <span class="up-vote"><i class="fas fa-angle-up"></i></span>
    <span class="number">990</span>
    <span class="down-vote"><i class="fas fa-angle-down"></i></span>
  </div>

</body>

</html>

3 个答案:

答案 0 :(得分:4)

我正在使用Prototype Pattern来组织代码。

这将适用于n中的.vote HTML类。例如,给定以下HTML,将创建两个投票对象并将其与各自的UI关联。

<div class="vote">
  <span class="up-vote"><i class="fas fa-angle-up"></i></span>
  <span class="number">0</span>
  <span class="down-vote"><i class="fas fa-angle-down"></i></span>
</div>
<div class="vote">
  <span class="up-vote"><i class="fas fa-angle-up"></i></span>
  <span class="number">0</span>
  <span class="down-vote"><i class="fas fa-angle-down"></i></span>
</div>

您可能已经注意到,以上id中没有HTMLid是在forEach循环中动态创建的,并在每个对象的init上分配。我使用myVotePrototype作为模板,将其prototype复制到在myVote中创建的每个新对象中。 myVote使用id进行初始化,这是每个投票都知道在哪里找到其关联的UI片段的方式。

那颜色呢?

JavaScript.vote父容器中设置投票方向。因此,在投票之后,该投票对象的HTML看起来像这样:

<div class="vote vote-up">
  <span class="up-vote"><i class="fas fa-angle-up"></i></span>
  <span class="number">0</span>
  <span class="down-vote"><i class="fas fa-angle-down"></i></span>
</div>

我添加了一点CSS,以便每个按钮都知道何时采用活动颜色。与直接将十六进制代码直接写到style属性中相比,我发现这比较麻烦。

.vote.vote-up .up-vote,
.vote.vote-down .down-vote {
  color: #3CBC8D;
}

演示

const myVotePrototype = {
  init: function(id) {
    this.voteId = id;
    // Prepare for voting clicks
    this.bindEvents();
  },
  votes: 0,
  upVote: function() {
    this.votes++;
    this.setVoteDirection('up');
  },
  downVote: function() {
    this.votes--;
    this.setVoteDirection('down');
  },
  setVoteDirection: function(direction) {
    let voteObj = document.getElementById(this.voteId);
    if (direction === 'up') {
      voteObj.classList.add('vote-up');
      if (voteObj.classList.contains('vote-down')) {
        voteObj.classList.remove('vote-down');
      }
    } else if (direction === 'down') {
      voteObj.classList.add('vote-down');
      if (voteObj.classList.contains('vote-up')) {
        voteObj.classList.remove('vote-up');
      }      
    }
  },
  updateUI: function() {
    document.querySelector(`#${this.voteId} .number`).innerHTML = Number(this.votes);
  },
  bindEvents: function() {
    document
      .querySelector(`#${this.voteId} .up-vote`)
      .addEventListener('click', () => {
        this.upVote();
        this.updateUI();
      });
    document
      .querySelector(`#${this.voteId} .down-vote`)
      .addEventListener('click', () => {
        this.downVote();
        this.updateUI();
      })
  }
};

function myVote(id) {
  function V() {};
  V.prototype = myVotePrototype;

  let v = new V();

  v.init(id);
  return v;
}

// Loop through all votes in the UI
const votes = document.querySelectorAll('.vote');
votes.forEach((vote, index) => {
  // Create an id
  let voteId = `vote_${index}`;
  // Set the id in the UI so we can find it later for updating
  vote.setAttribute('id', voteId);
  // Create a new vote object, passing in the vote id
  myVote(voteId);
});
.number {
  display: inline-block;
  text-align: center;
}

.vote {
  display: flex;
  flex-direction: column;
  text-align: center;
}

.up-vote,
.down-vote {
  color: dimgray;
  cursor: pointer;
}

.vote.vote-up .up-vote,
.vote.vote-down .down-vote {
  color: #3CBC8D;
}
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css" integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/" crossorigin="anonymous">

<div class="vote">
  <span class="up-vote"><i class="fas fa-angle-up"></i></span>
  <span class="number">0</span>
  <span class="down-vote"><i class="fas fa-angle-down"></i></span>
</div>
<div class="vote">
  <span class="up-vote"><i class="fas fa-angle-up"></i></span>
  <span class="number">0</span>
  <span class="down-vote"><i class="fas fa-angle-down"></i></span>
</div>
<div class="vote">
  <span class="up-vote"><i class="fas fa-angle-up"></i></span>
  <span class="number">0</span>
  <span class="down-vote"><i class="fas fa-angle-down"></i></span>
</div>

改进思想

  • 语义可访问的HTML<button>而非<span>
  • 使用ARIAbuttonnumber输出相关联
  • localStorage来记住页面加载之间的投票状态(或使用真实的DB:))

答案 1 :(得分:2)

我认为您会发现这更容易控制,并考虑是否要使用您在选项#1中提到的数组方法,然后创建一个单独的函数进行投票和一个单独的函数进行投票,而不是为每个单独的函数创建单独的函数像现在一样上下箭头。

您现在有四个组,因此在for循环中,我们可以像这样初始化数组:

const votes = [
  0: { up: false, down: false },
  1: { up: false, down: false },
  2: { up: false, down: false },
  3: { up: false, down: false },
];

然后,您可以仅调用上下投票功能,并检查该数组中与您单击的箭头的组号相对应的对象的值。

我认为有助于提高可读性的另一件事是将更改投票总数与更改颜色分开。

您的投票逻辑包括三种不同的可能性:1.)箭头已被选中; 2.)反向箭头已被选中; 3.)箭头均未被选中。但是,您的变色逻辑和用于更改每个箭头的检查值的逻辑实际上只有两种可能:要么之前检查过箭头,要么没有检查过箭头。

所以我继续进行下面的代码片段中的更改。

希望这会有所帮助。

const up_vote_spans = document.getElementsByClassName('up-vote');
const down_vote_spans = document.getElementsByClassName('down-vote');
const count = document.getElementsByClassName('number');

let votes = [];

for (let i = 0; i < count.length; i += 1) {
  const thisUpVoteSpan = up_vote_spans[i];
  const thisDownVoteSpan = down_vote_spans[i];
  votes[i] = { up: false, down: false };

  thisUpVoteSpan.addEventListener('click', handleUpvote.bind(null, i), false);
  thisDownVoteSpan.addEventListener('click', handleDownvote.bind(null, i), false);
}

function handleUpvote(i) {
  const currentVote = votes[i];
  const matchingUpSpan = up_vote_spans[i];
  const matchingDownSpan = down_vote_spans[i];
  const matchingCount = count[i];
  const currentCount = parseInt(matchingCount.innerHTML);

  if (currentVote.down) {
    matchingCount.innerHTML = currentCount + 2;
  } else if (currentVote.up === false) {
    matchingCount.innerHTML = currentCount + 1;
  } else {
    matchingCount.innerHTML = currentCount - 1;
  }
  if (!currentVote.up) {
    matchingUpSpan.style.color = "#3CBC8D";
    matchingDownSpan.style.color = 'dimgray';
    currentVote.up = true;
    currentVote.down = false;
  } else {
    matchingUpSpan.style.color = 'dimgray';
    currentVote.up = false;
  }
}

function handleDownvote(i) {
  const currentVote = votes[i];
  const matchingUpSpan = up_vote_spans[i];
  const matchingDownSpan = down_vote_spans[i];
  const matchingCount = count[i];
  const currentCount = parseInt(matchingCount.innerHTML);

  if (currentVote.up) {
    matchingCount.innerHTML = currentCount - 2;
  } else if (currentVote.down === false) {
    matchingCount.innerHTML = currentCount - 1;
  } else {
    matchingCount.innerHTML = currentCount + 1;
  }
  if (!currentVote.down) {
    matchingDownSpan.style.color = "#3CBC8D";
    matchingUpSpan.style.color = 'dimgray';
    currentVote.down = true;
    currentVote.up = false;
  } else {
    matchingDownSpan.style.color = 'dimgray';
    currentVote.down = false;
  }
}
.number {
  display: inline-block;
  text-align: center;
}

.vote {
  display: flex;
  flex-direction: column;
  text-align: center;
}

.up-vote {
  color: dimgray;
  cursor: pointer;
}

.down-vote {
  cursor: pointer;
  color: dimgray;
}
<!DOCTYPE html>
<html lang="en" dir="ltr">

<head>
  <meta charset="utf-8">
  <title></title>
  <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css" integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/" crossorigin="anonymous">
</head>

<body>

  <div class="vote">
    <span class="up-vote"><i class="fas fa-angle-up"></i></span>
    <span class="number">990</span>
    <span class="down-vote"><i class="fas fa-angle-down"></i></span>
  </div>
  <div class="vote">
    <span class="up-vote"><i class="fas fa-angle-up"></i></span>
    <span class="number">990</span>
    <span class="down-vote"><i class="fas fa-angle-down"></i></span>
  </div>
  <div class="vote">
    <span class="up-vote"><i class="fas fa-angle-up"></i></span>
    <span class="number">990</span>
    <span class="down-vote"><i class="fas fa-angle-down"></i></span>
  </div>
  <div class="vote">
    <span class="up-vote"><i class="fas fa-angle-up"></i></span>
    <span class="number">990</span>
    <span class="down-vote"><i class="fas fa-angle-down"></i></span>
  </div>

</body>

</html>

答案 2 :(得分:0)

我个人认为布尔型投票数组还不错。但是,如果您希望使用其他方法,则可以使用类操作来使它们分开以进行操作。

JSFiddle:https://jsfiddle.net/0uw83a17/

// From https://plainjs.com/javascript/traversing/get-siblings-of-an-element-40/
function getSiblings(el, filter) {
    var siblings = [];
    el = el.parentNode.firstChild;
    do { if (!filter || filter(el)) siblings.push(el); } while (el = el.nextSibling);
    return siblings;
}

function classFilter(el) {
    return el.classList && el.classList.contains('active-vote');
}

function handleVote(type, el) {
    let siblings = getSiblings(el, classFilter);

    if (el.classList.contains('active-vote')) {
        el.style.color = "dimgray";
        el.classList.remove('active-vote');
        return -1 * type;
    } else if (siblings.length === 1) {
        el.style.color = "#3CBC8D";
        siblings[0].style.color = "dimgray";
        siblings[0].classList.remove('active-vote');
        el.classList.add('active-vote');

        return 2 * type;
    } else if (!el.classList.contains('active-vote')) {
        el.style.color = "#3CBC8D";
        el.classList.add('active-vote');
        return type;
    }

    return 0;
}

const up_vote_span = document.getElementsByClassName('up-vote');
const down_vote_span = document.getElementsByClassName('down-vote');
const count = document.getElementsByClassName('number');

for (let i = 0; i < count.length; i++) {
  up_vote_span[i].addEventListener('click', function(e) {
      count[i].innerHTML = parseInt(count[i].innerHTML) + handleVote(1, up_vote_span[i]);
    });

  down_vote_span[i].addEventListener('click', function() {
      count[i].innerHTML = parseInt(count[i].innerHTML) + handleVote(-1, down_vote_span[i]);
  });
};