Skip welcome & menu and move to editor
Welcome to JS Bin
Load cached copy from
 
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Drag demo - mouse+pointer</title>
</head>
<body>
  <div id=cards>
    <div class=card><div class=icon>1</div></div>
    <div class=card><div class=icon>2</div></div>
    <div class=card><div class=icon>3</div></div>
    <div class=card><div class=icon>4</div></div>
    <div class=card><div class=icon>5</div></div>
    <div class=card><div class=icon>6</div></div>
  </div>
  <div id=circles>
    <div class=circle></div>
    <div class=circle></div>
    <div class=circle></div>
  </div>
<script>
  // Hand.js pointer event polyfill: http://handjs.codeplex.com/
(function () {
    // Polyfilling indexOf for old browsers
    if (!Array.prototype.indexOf) {
        Array.prototype.indexOf = function(searchElement) {
            var t = Object(this);
            var len = t.length >>> 0;
            if (len === 0) {
                return -1;
            }
            var n = 0;
            if (arguments.length > 0) {
                n = Number(arguments[1]);
                if (n != n) { // shortcut for verifying if it's NaN
                    n = 0;
                } else if (n != 0 && n != Infinity && n != -Infinity) {
                    n = (n > 0 || -1) * Math.floor(Math.abs(n));
                }
            }
            if (n >= len) {
                return -1;
            }
            var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
            for (; k < len; k++) {
                if (k in t && t[k] === searchElement) {
                    return k;
                }
            }
            return -1;
        };
    }
  
    // Installing Hand.js
    var supportedEventsNames = ["PointerDown", "PointerUp", "PointerMove", "PointerOver", "PointerOut", "PointerCancel", "PointerEnter", "PointerLeave",
                                "pointerdown", "pointerup", "pointermove", "pointerover", "pointerout", "pointercancel", "pointerenter", "pointerleave",
    ];
    var POINTER_TYPE_TOUCH = "touch";
    var POINTER_TYPE_PEN = "pen";
    var POINTER_TYPE_MOUSE = "mouse";
    // Touch events
    var generateTouchClonedEvent = function (sourceEvent, newName) {
        // Considering touch events are almost like super mouse events
        var evObj;
        if (document.createEvent) {
            evObj = document.createEvent('MouseEvents');
            evObj.initMouseEvent(newName, true, true, window, 1, sourceEvent.screenX, sourceEvent.screenY,
                sourceEvent.clientX, sourceEvent.clientY, sourceEvent.ctrlKey, sourceEvent.altKey,
                sourceEvent.shiftKey, sourceEvent.metaKey, sourceEvent.button, null);
        }
        else {
            evObj = document.createEventObject();
            evObj.screenX = sourceEvent.screenX;
            evObj.screenY = sourceEvent.screenY;
            evObj.clientX = sourceEvent.clientX;
            evObj.clientY = sourceEvent.clientY;
            evObj.ctrlKey = sourceEvent.ctrlKey;
            evObj.altKey = sourceEvent.altKey;
            evObj.shiftKey = sourceEvent.shiftKey;
            evObj.metaKey = sourceEvent.metaKey;
            evObj.button = sourceEvent.button;
        }
        // offsets
        if (evObj.offsetX === undefined) {
            if (sourceEvent.offsetX !== undefined) {
                // For Opera which creates readonly properties
                if (Object && Object.defineProperty !== undefined) {
                    Object.defineProperty(evObj, "offsetX", {
                        writable: true
                    });
                    Object.defineProperty(evObj, "offsetY", {
                        writable: true
                    });
                }
                evObj.offsetX = sourceEvent.offsetX;
                evObj.offsetY = sourceEvent.offsetY;
            }
            else if (sourceEvent.layerX !== undefined) {
                evObj.offsetX = sourceEvent.layerX - sourceEvent.currentTarget.offsetLeft;
                evObj.offsetY = sourceEvent.layerY - sourceEvent.currentTarget.offsetTop;
            }
        }
        // adding missing properties
        if (sourceEvent.isPrimary !== undefined)
            evObj.isPrimary = sourceEvent.isPrimary;
        else
            evObj.isPrimary = true;
        if (sourceEvent.pressure)
            evObj.pressure = sourceEvent.pressure;
        else {
            var button = 0;
            if (sourceEvent.which !== undefined)
                button = sourceEvent.which;
            else if (sourceEvent.button !== undefined) {
                button = sourceEvent.button;
            }
            evObj.pressure = (button == 0) ? 0 : 0.5;
        }
        if (sourceEvent.rotation)
            evObj.rotation = sourceEvent.rotation;
        else
            evObj.rotation = 0;
        // Timestamp
        if (sourceEvent.hwTimestamp)
            evObj.hwTimestamp = sourceEvent.hwTimestamp;
        else
            evObj.hwTimestamp = 0;
        // Tilts
        if (sourceEvent.tiltX)
            evObj.tiltX = sourceEvent.tiltX;
        else
            evObj.tiltX = 0;
        if (sourceEvent.tiltY)
            evObj.tiltY = sourceEvent.tiltY;
        else
            evObj.tiltY = 0;
        // Width and Height
        if (sourceEvent.height)
            evObj.height = sourceEvent.height;
        else
            evObj.height = 0;
        if (sourceEvent.width)
            evObj.width = sourceEvent.width;
        else
            evObj.width = 0;
        // PreventDefault
        evObj.preventDefault = function () {
            if (sourceEvent.preventDefault !== undefined)
                sourceEvent.preventDefault();
        };
        // Constants
        evObj.POINTER_TYPE_TOUCH = POINTER_TYPE_TOUCH;
        evObj.POINTER_TYPE_PEN = POINTER_TYPE_PEN;
        evObj.POINTER_TYPE_MOUSE = POINTER_TYPE_MOUSE;
        // Pointer values
        evObj.pointerId = sourceEvent.pointerId;
        evObj.pointerType = sourceEvent.pointerType;
        switch (evObj.pointerType) {// Old spec version check
            case 2:
                evObj.pointerType = evObj.POINTER_TYPE_TOUCH;
                break;
            case 3:
                evObj.pointerType = evObj.POINTER_TYPE_PEN;
                break;
            case 4:
                evObj.pointerType = evObj.POINTER_TYPE_MOUSE;
                break;
        }
        // If force preventDefault
        if (sourceEvent.currentTarget && sourceEvent.currentTarget.handjs_forcePreventDefault === true)
            evObj.preventDefault();
        // Fire event
        if (sourceEvent.target) {
            sourceEvent.target.dispatchEvent(evObj);
        } else {
            sourceEvent.srcElement.fireEvent("on" + getMouseEquivalentEventName(newName), evObj); // We must fallback to mouse event for very old browsers
        }
    };
    var generateMouseProxy = function (evt, eventName) {
        evt.pointerId = 1;
        evt.pointerType = POINTER_TYPE_MOUSE;
        generateTouchClonedEvent(evt, eventName);
    };
    var handleOtherEvent = function (eventObject, name) {
        if (eventObject.preventManipulation)
            eventObject.preventManipulation();
        for (var i = 0; i < eventObject.changedTouches.length; ++i) {
            var touchPoint = eventObject.changedTouches[i];
            var touchPointId = touchPoint.identifier + 2; // Just to not override mouse id
            touchPoint.pointerId = touchPointId;
            touchPoint.pointerType = POINTER_TYPE_TOUCH;
            touchPoint.currentTarget = eventObject.currentTarget;
            if (eventObject.preventDefault !== undefined) {
                touchPoint.preventDefault = function () {
                    eventObject.preventDefault();
                };
            }
            generateTouchClonedEvent(touchPoint, name);
        }
    };
    var getMouseEquivalentEventName = function (eventName) {
        return eventName.toLowerCase().replace("pointer", "mouse");
    };
    var getPrefixEventName = function (item, prefix, eventName) {
        var newEventName;
        if (eventName == eventName.toLowerCase()) {
            var indexOfUpperCase = supportedEventsNames.indexOf(eventName) - (supportedEventsNames.length / 2);
            newEventName = prefix + supportedEventsNames[indexOfUpperCase];
        }
        else {
            newEventName = prefix + eventName;
        }
        // Fallback to PointerOver if PointerEnter is not currently supported
        if (newEventName === prefix + "PointerEnter" && item["on" + prefix.toLowerCase() + "pointerenter"] === undefined) {
            newEventName = prefix + "PointerOver";
        }
        // Fallback to PointerOut if PointerLeave is not currently supported
        if (newEventName === prefix + "PointerLeave" && item["on" + prefix.toLowerCase() + "pointerleave"] === undefined) {
            newEventName = prefix + "PointerOut";
        }
        return newEventName;
    };
    var registerOrUnregisterEvent = function (item, name, func, enable) {
        if (enable) {
            item.addEventListener(name, func, false);
        } else {
            item.removeEventListener(name, func);
        }
    };
    var setTouchAware = function (item, eventName, enable) {
        // If item is already touch aware, do nothing
        if (item.onpointerdown !== undefined) {
            return;
        }
        // IE 10
        if (item.onmspointerdown !== undefined) {
            var msEventName = getPrefixEventName(item, "MS", eventName);
            registerOrUnregisterEvent(item, msEventName, function (evt) { generateTouchClonedEvent(evt, eventName); }, enable);
            // We can return because MSPointerXXX integrate mouse support
            return;
        }
        // Chrome, Firefox
        if (item.ontouchstart !== undefined) {
            switch (eventName.toLowerCase()) {
                case "pointerdown":
                    registerOrUnregisterEvent(item, "touchstart", function (evt) { handleOtherEvent(evt, eventName); }, enable);
                    break;
                case "pointermove":
                    registerOrUnregisterEvent(item, "touchmove", function (evt) { handleOtherEvent(evt, eventName); }, enable);
                    break;
                case "pointerup":
                    registerOrUnregisterEvent(item, "touchend", function (evt) { handleOtherEvent(evt, eventName); }, enable);
                    break;
                case "pointercancel":
                    registerOrUnregisterEvent(item, "touchcancel", function (evt) { handleOtherEvent(evt, eventName); }, enable);
                    break;
            }
        }
        // Fallback to mouse
        switch (eventName.toLowerCase()) {
            case "pointerdown":
                registerOrUnregisterEvent(item, "mousedown", function (evt) { generateMouseProxy(evt, eventName); }, enable);
                break;
            case "pointermove":
                registerOrUnregisterEvent(item, "mousemove", function (evt) { generateMouseProxy(evt, eventName); }, enable);
                break;
            case "pointerup":
                registerOrUnregisterEvent(item, "mouseup", function (evt) { generateMouseProxy(evt, eventName); }, enable);
                break;
            case "pointerover":
                registerOrUnregisterEvent(item, "mouseover", function (evt) { generateMouseProxy(evt, eventName); }, enable);
                break;
            case "pointerout":
                registerOrUnregisterEvent(item, "mouseout", function (evt) { generateMouseProxy(evt, eventName); }, enable);
                break;
            case "pointerenter":
                if (item.onmouseenter === undefined) { // Fallback to mouseover
                    registerOrUnregisterEvent(item, "mouseover", function (evt) { generateMouseProxy(evt, eventName); }, enable);
                } else {
                    registerOrUnregisterEvent(item, "mouseenter", function (evt) { generateMouseProxy(evt, eventName); }, enable);
                }
                break;
            case "pointerleave":
                if (item.onmouseleave === undefined) { // Fallback to mouseout
                    registerOrUnregisterEvent(item, "mouseout", function (evt) { generateMouseProxy(evt, eventName); }, enable);
                } else {
                    registerOrUnregisterEvent(item, "mouseleave", function (evt) { generateMouseProxy(evt, eventName); }, enable);
                }
                break;
        }
    };
    // Intercept addEventListener calls by changing the prototype
    var interceptAddEventListener = function (root) {
        var current = root.prototype ? root.prototype.addEventListener : root.addEventListener;
        var customAddEventListener = function (name, func, capture) {
            // Branch when a PointerXXX is used
            if (supportedEventsNames.indexOf(name) != -1) {
                setTouchAware(this, name, true);
            }
            if (current === undefined) {
                this.attachEvent("on" + getMouseEquivalentEventName(name), func);
            } else {
                current.call(this, name, func, capture);
            }
        };
        if (root.prototype) {
            root.prototype.addEventListener = customAddEventListener;
        } else {
            root.addEventListener = customAddEventListener;
        }
    };
    // Intercept removeEventListener calls by changing the prototype
    var interceptRemoveEventListener = function (root) {
        var current = root.prototype ? root.prototype.removeEventListener : root.removeEventListener;
        var customRemoveEventListener = function (name, func, capture) {
            // Release when a PointerXXX is used
            if (supportedEventsNames.indexOf(name) != -1) {
                setTouchAware(this, name, false);
            }
            if (current === undefined) {
                this.detachEvent(getMouseEquivalentEventName(name), func);
            } else {
                current.call(this, name, func, capture);
            }
        };
        if (root.prototype) {
            root.prototype.removeEventListener = customRemoveEventListener;
        } else {
            root.removeEventListener = customRemoveEventListener;
        }
    };
    // Hooks
    interceptAddEventListener(document);
    interceptAddEventListener(HTMLBodyElement);
    interceptAddEventListener(HTMLDivElement);
    interceptAddEventListener(HTMLImageElement);
    interceptAddEventListener(HTMLSpanElement);
    interceptAddEventListener(HTMLUListElement);
    interceptAddEventListener(HTMLAnchorElement);
    interceptAddEventListener(HTMLLIElement);
    if (window.HTMLCanvasElement) {
        interceptAddEventListener(HTMLCanvasElement);
    }
    if (window.SVGElement) {
        interceptAddEventListener(SVGElement);
    }
    interceptRemoveEventListener(document);
    interceptRemoveEventListener(HTMLBodyElement);
    interceptRemoveEventListener(HTMLDivElement);
    interceptRemoveEventListener(HTMLImageElement);
    interceptRemoveEventListener(HTMLSpanElement);
    interceptRemoveEventListener(HTMLUListElement);
    interceptRemoveEventListener(HTMLAnchorElement);
    interceptRemoveEventListener(HTMLLIElement);
    if (window.HTMLCanvasElement) {
        interceptRemoveEventListener(HTMLCanvasElement);
    }
    if (window.SVGElement) {
        interceptRemoveEventListener(SVGElement);
    }
    // Extension to navigator
    if (navigator.pointerEnabled === undefined) {
        // Indicates if the browser will fire pointer events for pointing input
        navigator.pointerEnabled = true;
        // IE
        if (navigator.msPointerEnabled) {
            navigator.maxTouchPoints = navigator.msMaxTouchPoints;
        }
    }
    // Handling touch-action css rule
    if (document.styleSheets && document.addEventListener) {
        document.addEventListener("DOMContentLoaded", function () {
            var trim = function (string) {
                return string.replace(/^\s+|\s+$/, '');
            };
            var processStylesheet = function (unfilteredSheet) {
                var globalRegex = new RegExp(".+?{.*?}", "m");
                var selectorRegex = new RegExp(".+?{", "m");
                while (unfilteredSheet != "") {
                    var block = globalRegex.exec(unfilteredSheet)[0];
                    unfilteredSheet = trim(unfilteredSheet.replace(block, ""));
                    var selectorText = trim(selectorRegex.exec(block)[0].replace("{", ""));
                    // Checking if the user wanted to deactivate the default behavior
                    if (block.replace(/\s/g, "").indexOf("touch-action:none") != -1) {
                        var elements = document.querySelectorAll(selectorText);
                        for (var elementIndex = 0; elementIndex < elements.length; elementIndex++) {
                            var element = elements[elementIndex];
                            if (element.style.msTouchAction !== undefined) {
                                element.style.msTouchAction = "none";
                            }
                            else {
                                element.handjs_forcePreventDefault = true;
                            }
                        }
                    }
                }
            }; // Looking for touch-action in referenced stylesheets
            try {
                for (var index = 0; index < document.styleSheets.length; index++) {
                    var sheet = document.styleSheets[index];
                    if (sheet.href == undefined) { // it is an inline style
                        continue;
                    }
                    // Loading the original stylesheet
                    var xhr = new XMLHttpRequest();
                    xhr.open("get", sheet.href, false);
                    xhr.send();
                    var unfilteredSheet = xhr.responseText.replace(/(\n|\r)/g, "");
                    processStylesheet(unfilteredSheet);
                }
            } catch (e) {
                // Silently fail...
            }
            // Looking for touch-action in inline styles
            var styles = document.getElementsByTagName("style");
            for (var index = 0; index < styles.length; index++) {
                var inlineSheet = styles[index];
                var inlineUnfilteredSheet = trim(inlineSheet.innerHTML.replace(/(\n|\r)/g, ""));
                processStylesheet(inlineUnfilteredSheet);
            }
        }, false);
    }
})();  
</script>
<script>
  %code%
</script>
  </body>
</html>
 
.card {
  background-color: blue;
  border: 1px solid black;
  width: 100px;
  height: 50px;
  display: inline-block;
  margin: 10px;
  cursor: hand;
  touch-action: none;
}
.card.moving {
  opacity: 0.7;
  pointer-events: none;
}
.icon {
  background-color: green;
  width: 40px;
  height: 40px;
  margin: 5px;
  box-sizing: border-box;
  padding: 10px;
}
.circle {
  background-color: yellow;  
  width: 50px;
  height: 50px;
  border: 1px solid black;
  display: inline-block;
  margin: 20px;
  border-radius: 25px;
}
.circle.open {
  background-color: orange;
  width: 70px;
  height: 70px;
  margin: 10px;
  border-radius: 35px;
}
 
var usePE = !!navigator.pointerEnabled;
var eventPrefix = usePE ? 'pointer' : 'mouse';
var activeCards = {};
function onStart(e) {
  var card = e.currentTarget;
  activeCards[usePE ? e.pointerId : 1] = card;
  if (!card.classList.contains('moving'))
  {
    card.classList.add('moving');
    card.startX = e.clientX;
    card.startY = e.clientY;
  }  
  e.preventDefault();
}
function onMove(e) {
  var card = activeCards[usePE ? e.pointerId : 1];
  if (card) {
    card.style.webkitTransform = 'translate(' +
      (e.clientX - card.startX) + 'px, ' +
      (e.clientY - card.startY) + 'px)';
    e.preventDefault();
  }
}
function onEnd(e) {
  var id = usePE ? e.pointerId : 1;
  var card = activeCards[id];
  if (card) {
    card.classList.remove('moving');  
    card.style.webkitTransform = '';
    e.preventDefault();
    if (e.target.classList.contains('circle'))
      removeCardFromCircle(card, e.target);
    delete activeCards[id];
  }
}
function onOver(e) {
  var card = activeCards[usePE ? e.pointerId : 1];
  if (card) {
    addCardToCircle(card, e.currentTarget);
  }
}
function onOut(e) {
  var card = activeCards[usePE ? e.pointerId : 1];
  if (card) {
    removeCardFromCircle(card, e.currentTarget);
  }
}
function addCardToCircle(card, circle)
{
  if (!circle.cardsOver)
    circle.classList.add('open');
  circle.cardsOver++;
}
function removeCardFromCircle(card, circle)
{
  circle.cardsOver--;
  if (!circle.cardsOver)
    circle.classList.remove('open');
}
var cards = document.querySelectorAll('.card');
for(var i = 0; i < cards.length; i++) {
  var c = cards[i];
  c.addEventListener(eventPrefix + 'down', onStart, false);
}
var circles = document.querySelectorAll('.circle');
for(var i = 0; i < circles.length; i++) {
  var c = circles[i];
  c.addEventListener(eventPrefix + 'over', onOver, false);
  c.addEventListener(eventPrefix + 'out', onOut, false);
  c.cardsOver = 0;
}
document.body.addEventListener(eventPrefix + 'move', onMove, false);
document.body.addEventListener(eventPrefix + 'up', onEnd, false);
if (usePE)
  document.body.addEventListener(eventPrefix + 'cancel', onEnd, false);
document.body.addEventListener(eventPrefix + 'out', function(e) {
  if (e.relatedTarget == document.documentElement)
    onEnd(e);
}, false);
Output 300px

You can jump to the latest bin by adding /latest to your URL

Dismiss x
public
Bin info
rbyerspro
0viewers