6
\$\begingroup\$

I've already written simple to do app using VanillaJS. Can you make any code review - JS? It also contain basic validation, if user type some thing two times.

(function() {
    "use strict";

    var addinput = document.getElementById("addinput"),
        addbtn = document.getElementById("addbtn"),
        todoList = document.getElementById("todo-list"),
        warning = document.getElementById("warning"),
        doneList = document.getElementById("done-list"),
        todoText = document.getElementById("todo-text"),
        todoTextDone = document.getElementById("todo-text-done");
    todoText.innerHTML = "You have no tasks to do.";
    todoTextDone.innerHTML = "You have not completed any task yet.";

    function displayInfoTodo() {
        var tdlNr = todoList.childNodes.length;
        todoText.innerHTML = (tdlNr < 1) ? "You have no tasks to do." : (tdlNr === 1) ? "You have <strong>1</strong> task to do." : "You have <strong>" + tdlNr + "</strong> tasks to do.";
    }

    function displayInfoDone() {
        var dlNr = doneList.childNodes.length;
        todoTextDone.innerHTML = (dlNr < 1) ? "You have not completed any task yet." : (dlNr === 1) ? "You have completed <strong>1</strong> task." : "You have completed <strong>" + dlNr + "</strong> tasks.";
    }

    function removeItem() {
        var item = this.parentNode.parentNode,
            removedItem = item.parentNode,
            removed = document.getElementById("removed"),
            remChild = removed.children;
        removedItem.removeChild(item);
        remChild.innerHTML = "";
        removed.style.display = "flex";
        remChild[0].innerHTML = "<a href='#' id='close'><i class='fa fa-close'></i></a> You removed&nbsp<strong>" + item.firstChild.textContent + "</strong>&nbspfrom list.";
        var close = document.getElementById("close");
        close.addEventListener("click", function(e) {
            e.preventDefault();
            var removed = document.getElementById("removed");
            removed.style.display = "none";
        });
        displayInfoTodo();
        displayInfoDone();
    }

    function doneItems() {
        var item = this.parentNode.parentNode,
            doneItem = item.parentNode,
            id = doneItem.id,
            target = (id === "todo-list") ? doneList : todoList;
        doneItem.removeChild(item);
        target.insertBefore(item, target.firstChild);
        displayInfoTodo();
        displayInfoDone();
    }

    function addItem(liText) {
        var li = document.createElement("li");
        li.innerHTML = "<span class='todo__body__list__item'>" + liText + "</span>";

        var buttons = document.createElement("span");
        buttons.classList.add("todo__body__list__buttons");

        var done = document.createElement("span");
        done.classList.add("todo__body__list__buttons--done");

        var remove = document.createElement("span");
        remove.classList.add("todo__body__list__buttons--remove");

        buttons.appendChild(done);
        buttons.appendChild(remove);
        li.appendChild(buttons);

        var todoListStr = todoList.innerHTML.toString().toLowerCase(),
            pattern = new RegExp("\\b" + liText.toLowerCase() + "\\b");
        if (todoListStr.match(pattern, "g")) {
            warning.style.opacity = 1;
            document.body.addEventListener("change", function() {
                warning.style.opacity = 0;
            });
        } else {
            todoList.insertBefore(li, todoList.firstChild);
            warning.style.opacity = 0;
        }

        addinput.value = "";
        remove.addEventListener("click", removeItem);
        done.addEventListener("click", doneItems);
        displayInfoTodo();
        displayInfoDone();
    }
    addinput.addEventListener("keyup", function(e) {
        e.preventDefault();
        if (e.keyCode == 13) {
            addbtn.click();
        }
    });
    addbtn.addEventListener("click", function() {
        var value = addinput.value;
        if (value) addItem(value);
    });

})();
html,
body,
div,
span,
applet,
object,
iframe,
h1,
h2,
h3,
h4,
h5,
h6,
p,
blockquote,
pre,
a,
abbr,
acronym,
address,
big,
cite,
code,
del,
dfn,
em,
img,
ins,
kbd,
q,
s,
samp,
small,
strike,
strong,
sub,
sup,
tt,
var,
b,
u,
i,
center,
dl,
dt,
dd,
ol,
ul,
li,
fieldset,
form,
label,
legend,
table,
caption,
tbody,
tfoot,
thead,
tr,
th,
td,
article,
aside,
canvas,
details,
embed,
figure,
figcaption,
footer,
header,
hgroup,
menu,
nav,
output,
ruby,
section,
summary,
time,
mark,
audio,
video {
  margin: 0;
  padding: 0;
  border: 0;
  font-size: 100%;
  font: inherit;
  vertical-align: baseline; }

/* HTML5 display-role reset for older browsers */
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
menu,
nav,
section {
  display: block; }

body {
  line-height: 1; }

ol,
ul {
  list-style: none; }

blockquote,
q {
  quotes: none; }

blockquote:before,
blockquote:after,
q:before,
q:after {
  content: '';
  content: none; }

table {
  border-collapse: collapse;
  border-spacing: 0; }

html,
body {
  background-image: url(../img/bg.jpg);
  background-position: center center;
  background-repeat: no-repeat;
  background-size: cover;
  color: #212121;
  font-family: 'Roboto', sans-serif;
  font-size: 20px;
  font-weight: 300;
  margin: 0 !important;
  padding: 0 !important;
  width: 100%;
  height: 100%;
  overflow-x: hidden; }

* {
  box-sizing: border-box; }

strong {
  font-weight: bold; }

.container {
  display: block;
  margin: auto;
  max-width: 1000px; }
  @media all and (max-width: 768px) {
    .container {
      padding: 0 10px; } }
  .container h2 {
    font-size: 34px;
    font-weight: bold;
    margin: 30px 0;
    text-align: center; }

.todo .todo__header, .todo .todo__header .todo__header__input, .todo .todo__header .todo__header__btn, .todo .todo__warning, .todo .todo__body .todo__body__h2, .todo .todo__body .todo__body__list li, .todo .todo__body .todo__body__list .todo__body__list__buttons .todo__body__list__buttons--remove,
.todo .todo__body .todo__body__list .todo__body__list__buttons .todo__body__list__buttons--done, .todo-column, .removed .removed__body, .removed .removed__body .fa {
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.23), 0 3px 10px rgba(0, 0, 0, 0.16); }

.todo {
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-direction: column; }
  .todo .todo__header {
    background-color: #7B1FA2;
    border-radius: 5px;
    max-width: 500px;
    margin: 0 auto 20px auto;
    width: 100%;
    padding: 15px;
    position: relative; }
    .todo .todo__header .todo__header__input {
      background-color: #9C27B0;
      border: #9C27B0;
      border-top-left-radius: 5px;
      border-bottom-left-radius: 5px;
      border-top-right-radius: 30px;
      border-bottom-right-radius: 30px;
      color: #fff;
      font-size: 20px;
      font-weight: 300;
      height: 60px;
      padding: 10px 70px 10px 10px;
      width: 100%; }
      .todo .todo__header .todo__header__input:focus {
        outline: none; }
    .todo .todo__header .todo__header__btn {
      background-color: #009688;
      border-radius: 50%;
      border: none;
      cursor: pointer;
      height: 60px;
      float: right;
      width: 60px;
      position: absolute;
      right: 15px;
      top: 15px;
      line-height: 60px;
      text-align: center; }
      .todo .todo__header .todo__header__btn:focus {
        outline: none; }
      .todo .todo__header .todo__header__btn:before {
        content: "\f067";
        color: #fff;
        font-family: FontAwesome;
        font-size: 29px;
        left: 0;
        position: absolute;
        top: 0;
        width: 100%; }
  .todo .todo__warning {
    background-color: #009688;
    border-radius: 5px;
    color: #fff;
    font-size: 16px;
    margin: 0 0 20px 0;
    opacity: 0;
    padding: 10px;
    transition: 0.5s ease-in; }
  .todo .todo__body {
    background-color: #E1BEE7;
    border-bottom-right-radius: 5px;
    border-bottom-left-radius: 5px;
    padding: 15px; }
    .todo .todo__body .todo__body__h2 {
      color: #fff;
      background-color: #009688;
      border-radius: 5px;
      font-size: 20px;
      padding: 10px;
      margin: 0 0 30px 0; }
      .todo .todo__body .todo__body__h2 .fa {
        float: right; }
    .todo .todo__body .todo__body__text {
      color: #212121;
      font-size: 16px;
      margin: 0 0 30px 0;
      text-align: center; }
    .todo .todo__body .todo__body__list {
      margin: 0;
      padding: 0; }
      .todo .todo__body .todo__body__list.todo__body__list--done .todo__body__list__buttons .todo__body__list__buttons--done {
        background-color: #009688; }
        .todo .todo__body .todo__body__list.todo__body__list--done .todo__body__list__buttons .todo__body__list__buttons--done:before {
          color: #fff; }
        .todo .todo__body .todo__body__list.todo__body__list--done .todo__body__list__buttons .todo__body__list__buttons--done:hover {
          background-color: #fff;
          transition: 0.2s ease-in; }
          .todo .todo__body .todo__body__list.todo__body__list--done .todo__body__list__buttons .todo__body__list__buttons--done:hover:before {
            color: #009688; }
      .todo .todo__body .todo__body__list li {
        display: flex;
        align-items: center;
        background-color: #9C27B0;
        border-radius: 5px;
        color: #fff;
        margin: 0 0 20px 0;
        min-height: 60px;
        padding: 10px;
        width: 100%; }
        .todo .todo__body .todo__body__list li:last-child {
          margin: 0; }
      .todo .todo__body .todo__body__list .todo__body__list__item {
        width: calc(100% - 100px);
        display: block;
        float: left; }
      .todo .todo__body .todo__body__list .todo__body__list__buttons {
        float: right;
        width: 90px;
        margin: 0 0 0 10px; }
        .todo .todo__body .todo__body__list .todo__body__list__buttons .todo__body__list__buttons--remove,
        .todo .todo__body .todo__body__list .todo__body__list__buttons .todo__body__list__buttons--done {
          border-radius: 50%;
          cursor: pointer;
          height: 40px;
          line-height: 40px;
          float: left;
          width: 40px;
          position: relative;
          text-align: center; }
          .todo .todo__body .todo__body__list .todo__body__list__buttons .todo__body__list__buttons--remove:before,
          .todo .todo__body .todo__body__list .todo__body__list__buttons .todo__body__list__buttons--done:before {
            color: #fff;
            font-family: FontAwesome;
            font-size: 20px;
            left: 0;
            position: absolute;
            top: 0;
            width: 100%; }
          .todo .todo__body .todo__body__list .todo__body__list__buttons .todo__body__list__buttons--remove:hover,
          .todo .todo__body .todo__body__list .todo__body__list__buttons .todo__body__list__buttons--done:hover {
            transition: 0.2s ease-in; }
            .todo .todo__body .todo__body__list .todo__body__list__buttons .todo__body__list__buttons--remove:hover:before,
            .todo .todo__body .todo__body__list .todo__body__list__buttons .todo__body__list__buttons--done:hover:before {
              color: #fff; }
        .todo .todo__body .todo__body__list .todo__body__list__buttons .todo__body__list__buttons--remove {
          background-color: #fff;
          margin: 0 0 0 10px; }
          .todo .todo__body .todo__body__list .todo__body__list__buttons .todo__body__list__buttons--remove:before {
            content: "\f014";
            color: #C2185B; }
          .todo .todo__body .todo__body__list .todo__body__list__buttons .todo__body__list__buttons--remove:hover {
            background-color: #C2185B; }
        .todo .todo__body .todo__body__list .todo__body__list__buttons .todo__body__list__buttons--done {
          background-color: #fff; }
          .todo .todo__body .todo__body__list .todo__body__list__buttons .todo__body__list__buttons--done:before {
            content: "\f00c";
            color: #009688; }
          .todo .todo__body .todo__body__list .todo__body__list__buttons .todo__body__list__buttons--done:hover {
            background-color: #009688; }

.todo-boxes {
  align-items: flex-start;
  flex-direction: row; }
  @media screen and (max-width: 768px) {
    .todo-boxes {
      align-items: center;
      flex-direction: column; } }

.todo-column {
  flex: 1;
  width: 100%;
  max-width: 45%;
  align-items: top;
  border-radius: 5px;
  margin: 0 0 15px 0; }
  @media screen and (max-width: 768px) {
    .todo-column {
      max-width: 500px;
      margin: 0 0 30px 0; } }
  .todo-column .todo__body {
    border-radius: 5px; }

.removed {
  display: none;
  align-items: center;
  justify-content: center;
  position: fixed;
  top: 0;
  left: 0;
  bottom: 0;
  right: 0;
  z-index: 10;
  background-color: rgba(0, 0, 0, 0.85); }
  .removed .removed__body {
    background-color: #9C27B0;
    border-radius: 5px;
    color: #fff;
    font-size: 34px;
    padding: 10px;
    width: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-direction: column;
    padding: 30px 0;
    position: relative; }
    @media screen and (max-width: 768px) {
      .removed .removed__body {
        width: 90%;
        font-size: 20px; } }
    .removed .removed__body strong {
      color: #E1BEE7;
      font-style: italic;
      margin: 10px 0;
      text-align: center;
      text-transform: uppercase; }
    .removed .removed__body .fa {
      position: absolute;
      right: 10px;
      top: 10px;
      background-color: #fff;
      border-radius: 50%;
      color: #009688;
      font-size: 20px;
      width: 40px;
      height: 40px;
      line-height: 40px;
      text-align: center; }

.copy {
  font-size: 14px;
  position: absolute;
  bottom: 10px;
  right: 30px; }

::-webkit-input-placeholder {
  /* Chrome/Opera/Safari */
  color: #fff;
  opacity: 0.5; }

::-moz-placeholder {
  /* Firefox 19+ */
  color: #fff;
  opacity: 0.5; }

:-ms-input-placeholder {
  /* IE 10+ */
  color: #fff;
  opacity: 0.5; }

:-moz-placeholder {
  /* Firefox 18- */
  color: #fff;
  opacity: 0.5; }
<div class="container">
        <h2>To Do App using VanillaJS</h2>
        <div class="todo">
            <div class="todo__header">
                <input type="text" class="todo__header__input" id="addinput" placeholder="Enter something...">
                <button type="submit" value="add" class="todo__header__btn" id="addbtn"></button>
            </div>
            <span class="todo__warning" id="warning">You've already added this task to todo list, oh come on :)</span>
        </div>
        <div class="todo todo-boxes">
            <div class="todo-column">
                <div class="todo__body">
                    <h2 class="todo__body__h2">I have to: <i class="fa fa-thumbs-o-down" aria-hidden="true"></i></h2>
                    <h3 class="todo__body__text" id="todo-text"></h3>
                    <ul class="todo__body__list" id="todo-list"></ul>
                </div>
            </div>
            <div class="todo-column">
                <div class="todo__body">
                    <h2 class="todo__body__h2">Completed tasks: <i class="fa fa-thumbs-o-up" aria-hidden="true"></i></h2>
                    <h3 class="todo__body__text" id="todo-text-done"></h3>
                    <ul class="todo__body__list todo__body__list--done" id="done-list"></ul>
                </div>
            </div>
        </div>
        <div id="removed" class="removed">
            <span class="removed__body"></span>
        </div>
    </div>
    <footer class="copy">&copy; 2017 Natalia Kiełbicka</footer>

\$\endgroup\$
2
\$\begingroup\$
<ul class="todo__body__list" id="todo-list"></ul>

Consider using BEM for naming your classes. It's a convention of naming sections of your UI in a way that's consistent, avoids structure dependency, and prevents CSS specificity from bloating. With BEM, you know the root of the section, where an element belongs, and what state it's in.

A general rule I follow is when I start to think of complicated element names or names that look like they're starting to nest 3 levels (i.e. todo__body__list), it's probably time to split that section off to a new block. In this case, your app would be todo which contains 3 other blocks.

<div class="todo">
  <div class="todo__upper"> <!-- still part of the "todo" block -->

    <!-- the header -->
    <div class="header">
      <div class="header__title">Hello!</div>
    </div>

  </div>
  <div class="todo__middle"> <!-- still part of the "todo" block -->

    <!-- the todo task list -->
    <div class="task-list">
      <ul class="task-list__things">
        <li class="task-list__thing">...</li>
      </ul>
    </div>

  </div>
  <div class="todo__lower"> <!-- still part of the "todo" block -->

    <!-- the done task list -->
    <div class="done-list">
      <ul class="done-list__things">
        <li class="done-list__thing">...</li>
      </ul>
    </div>

  </div>
  ....
</div>

html,
...
video {
  margin: 0;
  padding: 0;
  border: 0;
  font-size: 100%;
  font: inherit;
  vertical-align: baseline; }

I advise against a css "reset" as it blows away sane defaults for elements (i.e. Header margins and sizing, list indents, etc.). It's also not good if your app shares space with another app. CSS is global. If you stick a reset into an existing page, it will mess the existing page's styles. If your app also depends on a reset but for some reason you can't use it, then you mess your app.

Consider using normalize instead. It doesn't go as far as a reset, and only normalizes minor differences of defaults on all browsers.

If you really want to style starting with a blank slate, you're better off using <div>s for everything. I often do this myself to prototype things quickly without fear of style collision and just refactor elements later when there's time.

var addinput = document.getElementById("addinput"),
    addbtn = document.getElementById("addbtn"),
    todoList = document.getElementById("todo-list"),
    warning = document.getElementById("warning"),
    doneList = document.getElementById("done-list"),
    todoText = document.getElementById("todo-text"),
    todoTextDone = document.getElementById("todo-text-done");

Probably preference, but I suggest using a var per variable. The problem with commas is that you can't easily move around variables. For instance, try adding one more variable. You'll see that you have to remove ; from the last one. Also, if say you want to remove the last one, you have to replace the previous line's , into an ;. I'd trade away how the code looks in favor of getting rid of minor annoyances.

@media screen and (max-width: 768px) {

Design mobile first, not last. This means design for mobile, then use min-width to override it for the larger screen styles, not the other way around (where you style for desktop and patch in media queries for mobile. The benefit is that doing mobile first, mobile devices can ignore parsing of styles for larger screens, equating to less work on the device.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ I thought, that I write using BEM :) Can you tell what should be the proper class for <ul class="todo__body__list" id="todo-list"></ul>? Thanks a lot for remaining tips. \$\endgroup\$ – Natalia Aug 28 '17 at 19:35
  • \$\begingroup\$ @Natalia Updated answer. \$\endgroup\$ – Joseph Aug 28 '17 at 20:20
  • \$\begingroup\$ @Natalia Also, element names don't nest. Think of them as a single-level list under the block name. That means there can only be one __ after the block name (i.e. todo__items, todo__item, todo__linkeven if the first is the <ul>, the second is the <li> and the third is the <a>) \$\endgroup\$ – Joseph Aug 28 '17 at 20:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.