13

I have the following code which will allowed a user running iOS to move a <div> with the class .drag around on the page. This works fine when there is one istance of .drag, but fails to work when there are two instances of it. Is it possible to have the code find all of the <div>'s, then allow them to be draggable?

var drag = $(".drag")[0];
    xPos = drag.offsetWidth / 2;
    yPos = drag.offsetHeight / 2;
    drag.addEventListener("touchmove", function() {
        event.preventDefault();
        $(this).css({
        'left' : event.targetTouches[0].pageX - xPos + 'px', 
        'top' : event.targetTouches[0].pageY - yPos + 'px'
    });
});
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Charlie
  • 11,380
  • 19
  • 83
  • 138

4 Answers4

17

When you use $(selector)[0], you get the first DOM element that matches the selector. Use .each() instead to add the event listener to all elements that match the selector:

$(".drag").each(function () {
    var drag = this;
    xPos = drag.offsetWidth / 2;
    yPos = drag.offsetHeight / 2;
    drag.addEventListener("touchmove", function() {
        event.preventDefault();
        $(this).css({
          'left' : event.targetTouches[0].pageX - xPos + 'px', 
          'top' : event.targetTouches[0].pageY - yPos + 'px'
        });
    });
});​
João Silva
  • 89,303
  • 29
  • 152
  • 158
  • 3
    It is not good practice to mix jQuery with JavaScript. You should use `var drag = $(this)` instead of `var drag = this`. – brienna Apr 07 '18 at 20:28
1

Yes, it's possible. But you are using a jQuery selector (which can select and return multiple elements) and then immediately unwrapping it to return the first element. You can modify your code to use jQuery functions throughout and avoid this.

// an array of all elements with class "drag"
// each element is wrapped
var drag = $(".drag");

// selects all matching elements, but then references
// the first raw DOM element in the array
var drag = $(".drag")[0]; 

Another way of looking at it:

var matches = $(".drag");

// each() executes a function for each matched element
matches.each(function () {
    var drag = this; // raw dom element

    // or, wrap to get jQuery object
    // var drag = $(this);
});​

As I mentioned, you can also use jQuery functions throughout your code. Two quick examples I see are the x/y coordinate calculation and the event binding.

Tim M.
  • 53,671
  • 14
  • 120
  • 163
  • I recieve the following error when I do that: `TypeError: 'undefined' is not a function (evaluating 'drag.addEventListener')`. You can view a live example here: http://codekraken.com/testing/drag/test.html (must be running iOS to have it work). Also, can you elaborate on using jQuery functions instead of what I currently have? – Charlie Sep 09 '12 at 01:40
  • Yes, his works. Does your example not work because `var drag = $(".drag");` would be assigning more than one element to the variable? – Charlie Sep 09 '12 at 01:43
  • He and I are saying the same thing...I'm just explaining how the selectors work. I added another example to clarify. – Tim M. Sep 09 '12 at 01:47
  • Thanks for the clarification. Which way is better? I know that's a general question, but as far as readability, I prefer João's answer. Also, if you guys are saying the same thing, why does your example not work while his does? – Charlie Sep 09 '12 at 01:52
  • Because I didn't give a complete example like João did. I just showed you the theory behind what you were doing wrong, i.e. trying to use only the first element matched by a selector. As far as the best way, there are many valid ways to use jQuery. I'd start by reading up on the each() method: http://api.jquery.com/each/ – Tim M. Sep 09 '12 at 01:56
0

First, you declare that you want only the first element by using [0].

Second, you should use jQuery's on() method. Here's how I see you function:

var drag = $(".drag");

drag.on("touchmove", function(event) {
    xPos = $(this).offsetWidth / 2;
    yPos = $(this).offsetHeight / 2;

    event.preventDefault(); // preventDefault is IE-specific, is it?  

    $(this).css({
        'left' : event.targetTouches[0].pageX - xPos + 'px', 
        'top' : event.targetTouches[0].pageY - yPos + 'px'
        });
});
mhaligowski
  • 2,182
  • 20
  • 26
  • What are the benefits of using the `bind()` method? – Charlie Sep 09 '12 at 01:48
  • Sorry, my mistake - since jQuery 1.7 the preferred method is 'on' instead of 'bind'. addEventListener won't even work here, (and neither should work in the solution marked as correct), as addEventListener is for JS Element, and you're trying to bind it to a jQuery Object. You can find discussion on using 'on' here: http://stackoverflow.com/questions/8996015/jquery-on-vs-javascript-addeventlistener – mhaligowski Sep 10 '12 at 06:31
0

This is probably obvious to most experienced devs, but could be helpful to some junior devs who like myself have had trouble getting jQuery to apply to multiple elements.

Today I learned that you have to make sure you’re loading your script outside the div itself.

I was mistakenly loading the script inside the first element, and wondering why the jQuery function wasn’t applying to the other div farther down the page.

Screenshot showing < script > inside parent < div > ^ I was doing it wrong