[Must Win] Write a landlord card recorder

Posted May 26, 20206 min read


Recently, I played a few landlords in my spare time. Unfortunately, I did n t have the money to buy a card recorder. I could n t remember so many cards in my mind. Then write a card recorder and change your life!


Needless to say, it must be the most practical and beautiful to imitate the landlord's card recorder.
ps:This is a screenshot of the final draft. There are some buttons in the first few editions. Why you can see it after removing it.


First of all, how to design the interaction of this card recorder is the key, because we can only manually record the cards, and we will subtract the cards that have been discarded from a deck of cards.

  • Idea 1:keyboard input, press Enter

    • If it is 1-9, it must be very fast, but the JQK size king is too troublesome to input. If it is replaced with continuous letters on other keyboards to replace the JQK size king, then the cost of memory is required. There is also a card recorder. ? !
  • Idea 2:Click to select, and then press the Enter button

    • This is actually more in line with the operation logic of our landlord, but there is a problem. If I m out of an airplane, I have to order a lot, which is very troublesome! Not very good.
  • Idea 3:Click + slide to directly play cards, remove the carriage return or button, right click to withdraw

    • This is the final interaction logic. If the opponent only produces one, then we only need to click on it. If we leave the company and the plane, then we only need to slide a few times. Because the card button is removed, it is easy to make a mistake. Here, a right button is added to withdraw a card.


It is easy to come up with ideas:

  1. Draw the interface, 13 kinds of cards(4 cards/type) + size king(2 cards, there is no distinction between size kings here, because generally I am only afraid of bombs, size king is missing a afraid hammer, and missing a In the future, whether it is the king or the king is the biggest, is there a difference?)
  2. Add left-click events and left-click slide events.
  3. Block the default right-click event and add a right-click event.
  4. Do some details, such as 4 cards red warning, 0 cards, the card face becomes gray and so on.

Implementation 1:Painting the interface

It's too simple here, I don't want to say it. You can write dozens of divs manually or add them dynamically with js.

Implementation 2:Left click and slide processing

We should be divided into two cloths here, the first step is to choose, the second step is to play cards. So we can use an array to store the serial numbers of the cards we have selected.

  • Left click

    • Add the serial number of this card directly into the array
    • Then play
  • Slide

    • Two methods, one is to use coordinate positioning to determine which cards are included in the distance before and after mouse movement to decide which cards should be selected. The second one is to add mouseenter, mouseleave events to each card. When the mouse passes, it will be triggered, and the serial number of the starting card will be added to the array.
    • Then play

Implementation 3:Right click to increase the number of cards

The only thing to note here is the following, first shield the default right-click event.

document.oncontextmenu = function(event) {
      if(event.button == 2) {

Then just deal with event.target.

The difference between target and currentTarget is:the former may be a child element under the binding event element, the latter is the binding event element itself. Pay attention to the difference.

Implementation 4:State change

You see our interface design before, when there are 4 cards, it is red and bold, indicating that there may be a bomb; when there is no bomb, it is gray; when the card is finished, it is light gray with transparency.
Here we only need to change the state according to the number of cards. The key is how can we change the number of cards to be more convenient?

You might use innerHTML, innerText?

The before and after in the pseudo-elements in CSS have a content attribute, and this attribute can be bound to the custom attribute on the element. For example

.card ::after {
      color:# 999;

Then the value of this content will follow the count attribute of the element. Therefore, we only need to change the value of this attribute for the card operation, and then add and delete some corresponding class names according to the value.

For people

  • No money to buy a card recorder
  • Poor memory
  • Play on computer


After using this card recorder, the win rate is not much higher, Gan!
I found out that I was out of luck. Others beat "super double". After landing, I would receive thousands of beans and return to the west!

Follow-up may write a luck changer!


Of course not. Gan!

Source code

Many notes have been added to the source code. Just copy and paste, and it may be put on the blog to provide download links in the future.

<! DOCTYPE html>
<html lang = "en">

  <meta charset = "UTF-8">
  <meta name = "viewport" content = "width = device-width, initial-scale = 1.0">
  <title> Card reader </title>
    #box {

    .card {
      background-color:# f5f5f5;
      box-shadow:0 5px 5px 0 # c9c9c9;

    .card--empty {
      color:# c3c3c3;

    .card--chosen {

    .card ::after {
      color:# 999;

    .card--warning ::after {

    # btn-box {

    button {
      padding:10px 30px;
      transition:all ease .3s;

    button:hover {

    .info {
      color:# 999;

  <div id = "box"> </div>
  <div class = "info">
    <P> Author:<a href="http://www.leelei.info"> leelei </a> </p>
    <p> Usage:Use the mouse [click]to play cards directly, or [click to slide]to play cards, right-click a card to increase the number of cards(used to prevent mistakes) </p>
    <p> Applicable to:novice landlords, and do n t have money to buy card readers, and often use mobile phones to play landlords while driving computers </p>
  const arr = [3, 4, 5, 6, 7, 8, 9, 10, 'J', 'Q', 'K', 'A', 2, '?'];
  const nums = [4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2];
  const TYPE_CHOSEN = 'card--chosen';
  const TYPE_WARNING = 'card--warning';
  const TYPE_EMPTY = 'card--empty';
  //Storage has been selected
  let chosenArr = [];

  //Add class name
  function addClass(e, name) {
    if(e.className.indexOf(name) == -1) {
      e.className + = name;
  //Remove the class name
  function removeClass(e, name) {
    e.className = e.className.replace(name, '')
  //Initialize the card
  function initCards() {
    let box = document.getElementById('box');
    let frag = document.createDocumentFragment();
    for(let i = 0; i <14; i ++) {
      let ele = document.createElement('div');
      ele.className = 'card' + TYPE_WARNING;
      ele.innerText = arr [i];
      ele.setAttribute('count', nums [i]);
      ele.setAttribute('index', i);

  //Initialize input
  function initSlideInput() {
    let cards = document.getElementsByClassName('card');
    //Register when the mouse is pressed
    box.addEventListener('mousedown', installSlideInput)
    //Mouse up to uninstall
    box.addEventListener('mouseup', uninstallSlideInput)
    box.addEventListener('mouseleave', uninstallSlideInput)
    //Register sliding input
    function installSlideInput() {
      Array.from(cards) .forEach((e) => {
        e.addEventListener('mouseenter', slideSelect)
        e.addEventListener('mouseleave', slideSelect)
    //Uninstall slide input
    function uninstallSlideInput() {
      Array.from(cards) .forEach((e) => {
        e.removeEventListener('mouseenter', slideSelect)
        e.removeEventListener('mouseleave', slideSelect)
    //Click to enter, it is always valid
    Array.from(cards) .forEach((e) => {
      e.addEventListener('click', function(event) {
    //slide input
    function slideSelect(event) {
      let idx = event.currentTarget.getAttribute('index');
      if(event.currentTarget.className.indexOf(TYPE_CHOSEN) == -1) {
        addClass(event.currentTarget, TYPE_CHOSEN)

  //Reset the status of all cards, raised-> aligned
  function reset() {
    chosenArr = [];
    let cards = document.getElementsByClassName('card');
    Array.from(cards) .forEach((v, i) => {
      removeClass(v, TYPE_CHOSEN)

  //Play cards
  function submit() {
    let cards = document.getElementsByClassName('card');
    chosenArr.forEach((v, i) => {
      cards [v].setAttribute('count', cards [v].getAttribute('count')-1);
      removeClass(cards [v], TYPE_WARNING);
      if(cards [v].getAttribute('count') <1) {
        //Minimum is 0, set to empty state
        cards [v].setAttribute('count', 0);
        addClass(cards [v], TYPE_EMPTY)
    //Reset state
  //Right click to add cards to prevent wrong cards
  function addCardCount(e) {
    if(e.getAttribute('count')! = undefined) {
      e.setAttribute('count', e.getAttribute('count')-0 + 1);
    if(e.getAttribute('count')> = nums [e.getAttribute('index')]) {
      e.setAttribute('count', nums [e.getAttribute('index')]);
      addClass(e, TYPE_WARNING)
    } else if(e.getAttribute('count')> 0) {
      removeClass(e, TYPE_EMPTY);

  window.onload = function() {
    document.oncontextmenu = function(event) {
      if(event.button == 2) {