5

My jQuery Script:

My jQuery script is quite a large file however I have trimmed it down to the most relevant parts for this question as seen below;

$(document).ready(function() {
    "use strict";

    $(document).on('click', function(e) {

        if (($(e.target).attr('data-action')) || $(e.target).parent().data('action')) {

            if ($(e.target).attr('data-action')) {
                action = $(e.target).data('action');
            } else {
                action = $(e.target).parent().data('action');
            }

            switch (action) {

                case 'mail-pin':
                    // Code for 'mail-pin' click
                    break;

                default:
                    return;
            }
        }
    });
});

My HTML Structure:

<!-- === INBOX LIST STARTS === -->
<div class="inbox-content clearfix">
    <div class="Head">
        <!-- Code for inbox header -->
    </div>
    <div class="Body clearfix">
        <div class="Pinned">
            <div class="Mail clearfix" data-mail-ID="1234">
                <!-- Mail Item -->
            </div>
            <div class="Mail clearfix" data-mail-ID="1235">
                <!-- Mail Item -->
            </div>
        </div>
        <div class="Standard">
            <div class="Mail clearfix" data-mail-ID="1233">
                <!-- Mail Item -->
            </div>
            <div class="Mail clearfix" data-mail-ID="1236">
                <!-- Mail Item -->
            </div>
        </div>
    </div>
</div>

Question

First of all, I know how to check where the item is to be moved either from the .Pinned element or the .Standard to the other element via such means seen below;

case 'mail-pin':
    console.log('Hello');
    if ($(e.target).closest('.Mail').parent().hasClass('Pinned')) {
        console.log('It is pinned.');
    } else {
        console.log('It is not pinned.');
    }
    break;

What I don't understand how to achieve is how to append the content in the correct location and by this I refer to the order taken from the data-mail-ID="1233" so that upon clicking to either pin or unpin, the content will be appended to the place, so if you click to pin mail ID X it will append after X - 1 and visa versa if the item is unpinned.

Important Note:

Each list only displays 20 items per page and upon clicking to go to the next or previous page, the page fetches the content which would have been modified upon clicking to pin or unpin therefore this script would only be relevant for those ID's which can be found on that page, thus meaning if you unpin ID 123 and 122 cannot be found, it is simply removed and for pinning, the append part would only occur if .Pinned is visible otherwise that item just removes.

Tyler
  • 854
  • 1
  • 10
  • 26

1 Answers1

1

Search the first mail with a larger id and append the clicked element before it.

(The function below uses a closure to store the relevant DOM parts so I only need to query the DOM once. It's not really needed but that's the way I'm doing such things^^)

var togglePinState = (function () {
    var pinned = document.querySelector(".Pinned"),
        unpinned = document.querySelector(".Standard"),
        pinnedMails = pinned.getElementsByClassName("Mail"),
        unpinnedMails = unpinned.getElementsByClassName("Mail");
        // .getElementsByClassName() because it returns a live HTMLCollection
        // pinnedMails and unpinnedMails will always have the currently un/pinned "mails" without re-querying the DOM

    return function (mailItem) {
        var mailId = parseInt(mailItem.getAttribute("data-mail-ID"), 10),
            mailItemIsPinned = (mailItem.parentNode.className === "Pinned"),
            newParent = (mailItemIsPinned ? unpinned : pinned),
            mailsToCheck = (mailItemIsPinned ? unpinnedMails : pinnedMails),
            // variables for the loop below
            i = 0,
            l = mailsToCheck.length,
            currentId;

        for (; i < l; i++) {
            currentId = parseInt(mailsToCheck[i].getAttribute("data-mail-ID"), 10);
            if (currentId > mailId) {
                // insert before first pinned mail with a bigger id
                mailsToCheck[i].insertAdjacentElement("beforebegin", mailItem);
                return;
            }
        }

        // at this point we have not found a mail with a bigger id so we can safely append it at the end of the list
        newParent.appendChild(mailItem);
    }
}());

To use it in your script just call it in the mail-pin branch:

case 'mail-pin':
    togglePinState(e.target);
    break;

And here is the function in action:

    var togglePinState = (function() {
      var pinned = document.querySelector(".Pinned"),
        unpinned = document.querySelector(".Standard"),
        pinnedMails = pinned.getElementsByClassName("Mail"),
        unpinnedMails = unpinned.getElementsByClassName("Mail");
      // .getElementsByClassName() because it returns a live HTMLCollection
      // pinnedMails and unpinnedMails will always have the currently un/pinned "mails" without re-querying the DOM

      return function(mailItem) {
        var mailId = parseInt(mailItem.getAttribute("data-mail-ID"), 10),
          mailItemIsPinned = (mailItem.parentNode.className === "Pinned"),
          newParent = (mailItemIsPinned ? unpinned : pinned),
          mailsToCheck = (mailItemIsPinned ? unpinnedMails : pinnedMails),
          // variables for the loop below
          i = 0,
          l = mailsToCheck.length,
          currentId;

        for (; i < l; i++) {
          currentId = parseInt(mailsToCheck[i].getAttribute("data-mail-ID"), 10);
          if (currentId > mailId) {
            // insert before first pinned mail with a bigger id
            mailsToCheck[i].insertAdjacentElement("beforebegin", mailItem);
            return;
          }
        }

        // at this point we have not found a mail with a bigger id so we can safely append it at the end of the list
        newParent.appendChild(mailItem);
      }
    }());

document.querySelector("div.inbox-content")
     .addEventListener("click", function(e) {
           if (e.target.nodeName === "DIV" && e.target.classList.contains("Mail")) {
             togglePinState(e.target);
            }
          });
div { padding: 2px }
div.Mail {
  border: dotted 1px black;
  text-align: center;
}
.Pinned { border: solid 1px red }
.Standard { border: solid 1px blue }
<!-- === INBOX LIST STARTS === -->
<div class="inbox-content clearfix">
  <div class="Head">
    <!-- Code for inbox header -->
  </div>
  <div class="Body clearfix">
    <div class="Pinned">
      <div class="Mail clearfix" data-mail-ID="1234">1234</div>
      <div class="Mail clearfix" data-mail-ID="1237">1237</div>
    </div>
    <div class="Standard">
      <div class="Mail clearfix" data-mail-ID="1233">1233</div>
      <div class="Mail clearfix" data-mail-ID="1235">1235</div>
      <div class="Mail clearfix" data-mail-ID="1236">1236</div>
      <div class="Mail clearfix" data-mail-ID="1238">1238</div>
    </div>
  </div>
</div>
Andreas
  • 21,535
  • 7
  • 47
  • 56
  • Hiya and thank you for this @Andreas. Does your script have an 'unpin' function? I noticed the snippet doesn't seem to, just got back home and only have a brief glance so far. I appreciate your time and effort and once I've taken more of a deeper look, I'll rate and award. – Tyler Nov 12 '16 at 14:48
  • Going through your code, this appears to be the next level up compared to my current programming skills with jQuery. Working with your snippet now to see what I can achieve. – Tyler Nov 12 '16 at 15:43
  • Just one problem I've found so far is that placing this within my case 'mail-pin': it requires to click on any pin twice for this to work and afterwards you can click on any pin once for this to move to the pinned area. I also need to make `this > .Actions > .Pin` to display upon being pinned. – Tyler Nov 12 '16 at 16:18
  • 1
    @TimMarshall I've adjusted the script to also unpin mails and added a part on how to use the function – Andreas Nov 12 '16 at 17:12
  • All works, I just had to adjust `togglePinState(e.target);` to `togglePinState(e.target.closest('.Mail'));` – Tyler Nov 13 '16 at 14:29