Apply jQuery events to all class elements? - javascript

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'
});
});

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'
});
});
});​

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.

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'
});
});

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

Related

Getting currently clicked co-ordinates relative to an element

i have a function to get the co-ordinates of the recently clicked position with respect to a div element without using JQuery which is working fine .
without jquery (working) :
var Board = document.getElementById("board"); // div element
function mouseListener(e)
{
var posX = e.clientX - Board.getBoundingClientRect().left,
posY = e.clientY - Board.getBoundingClientRect().top
console.log(posX+" : "+posY);
}
window.addEventListener("mousedown", mouseListener,false);
But this one uses JQuery and giving different co-ordinates as compared to the previous code
using jquery(not working) :
var Board = $("#board");
function mouseListener(e)
{
var posX = e.clientX - Number(Board.css("left").replace("px", "")),
posY = e.clientY - Number(Board.css("top").replace("px", ""));
console.log(posX+" : "+posY);
}
$(window).mousedown(mouseListener);
How to write this properly on jQuery so that it'll work like the first code?
With jQuery you have to use .offset() to get the same values as with .getBoundingClientRect():
function mouseListener(e) {
var posX = e.clientX - parseFloat(Board.offset().left),
posY = e.clientY - parseFloat(Board.offset().top);
console.log(posX+" : "+posY);
}
jQuery.fn.offset() Reference here;
There are three ways to attach the event-handler. Used in jQuery < 1.7 but still working in newer versions:
$(window).bind('mousedown', mouseListener);
As of jQuery 1.7 method .on() is used, it has the greatest flexibility also for event delegation:
$(window).on('mousedown', mouseListener);
The third is what you have used, it's only a shortcut and calls internally .on().
Reference jQuery.fn.bind() - - - - Reference jQuery.fn.on()
Whatever you have done is absolutely correct logicwise, just minor syntactical mistakes.
var Board = $("#board");
function mouseListener(e)
{
var posX = e.clientX - Number(Board.css("left").replace("px", "")), //you forgot to put closing braces here and in the next line.
posY = e.clientY - Number(Board.css("top").replace("px", ""));
console.log(posX+" : "+posY);
}
$(window).mousedown(mouseListener);
Make sure you have used style left and top in the element with id "#board" else output will be NaN : NaN.

jquery click event on array of objects versus trigger click on single element

I understand jquery and have worked with it but I am by no means a pro thus this question.
I have a complicated jquery array object defined like this
var $elements = $([]);
$elements = $elements.add($protoElement.clone()
.css({
left: offset.left,
top: offset.top,
backgroundPosition: (bgLeft + 'px ' + bgTop + 'px')
})
...
Later on I add to it a click event
$elements.click(function() {
....
var $element = $(this);
....
}
This is all code that I "inherited". I need to extend/change the code so that
the click event is triggered by code (rather then by an explicit click) on a specific $element of the array $elements.
What is the best way to achieve that?
Will there be a conflict between the code click trigger for an element and the above click event handler?

in gamequery I am trying to move a selected object with mousetracker by clicking on the object, and dragging it

I know I can use mousedown selection for it, but I am wanting the clicked on sprite to follow my mouse, there is a mousetracker function of sorts mentioned in the api; but unfortunately there are no examples of this other than stating that it allows mouse detection.
//add mousedown events for yarnballs.
$(".gQ_sprite").mousedown(function() {
clickedDivId = $(this).attr('id');
if(clickedDivId.charAt(8) == "-")
{
currentClickedDivId = clickedDivId
$(document).mousemove(function(e){
spriteXPosition = e.pageX
spriteYPosition = e.pageY
});
}
});
I have the location of the mouse selected, just not sure how to get the selected sprite to follow it.
any help would be greatly appreciated.
What Mati said is correct: $.gQ.mouseTracker allows you to access the mouse's state outside of an event handler. The example he gives is correct but it can't be used to move a gQ object (sprite, tile-map or group) around because you'r not allowed to use the .css() function for those. Doing so will break collision detection.
If what you want is to move a gQ object you should do this instead :
$('#' + currentClickedDivId).xy($.gQ.mouseTracker.x, $.gQ.mouseTracker.y);
But since this should be done in a periodical callback, the smoothness of the dragging will depend on the refresh rate.
If you want to use event handlers instead you could modify you code to look like this (without using the mouseTracker):
var clickedDiv;
var clickedDivOffset = {x:0, y:0};
$(".gQ_sprite").mousedown(function(e) {
clickedDiv = $(this);
clickedDivOffset = {
x: e.pageX - clickedDiv.x() - $().playground().offset().left,
y: e.pageY - clickedDiv.y() - $().playground().offset().top
};
});
$(".gQ_sprite").mouseup(function() {
clickedDiv = false;
});
$().playground().mousemove(function(e) {
if(clickedDiv){
clickedDiv.xy(
e.pageX - clickedDivOffset.x,
e.pageY - clickedDivOffset.y,
);
}
});
This will implement a drag-n-drop effect. If you want the clicked element to stick to the mouse you will have to slightly adapt the code but the basics will remain the same.
According to the documentation:
If the mouse tracker is enabled you can check the state of the mouse at anytime by looking into the object $.gQ.mouseTracker where x and y contain the position of the mouse and 1, 2 and 3 a boolean value where true means that the first, second or thrid button is pressed.
Observe the output of:
$("#playground").playground({ refreshRate: 60, mouseTracker: true });
$.playground().startGame();
$.playground().registerCallback(function(){
console.log( $.gQ.mouseTracker );
}, 1000);
To make those divs actually follow the cursor, you have to use .css()
$('#' + currentClickedDivId).css({
top: $.gQ.mouseTracker.y + 'px',
left: $.gQ.mouseTracker.x + 'px'
});

How to change the left attribute on page resize (jQuery)

I'm having slight troubles with my code. What I'm trying to do is make these element's css property 'left' update according to the difference of it's current left value, and the amount the page resizes. This way, when the page resizes and the background moves over, the elements will move too. Take a look at the code below and I'll describe the issue:
$(window).resize(function() {
var docWidth = $(window).width();
if (docWidth < 1000) {
var difference = 1000-docWidth;
$('#headNav a,#icons div').each(function() {
var left = $(this).position().left;
var newLeft = left - difference;
$(this).css({ 'left' : newLeft });
});
}
});
So the issue that I'm getting is the elements are being given left values of wild numbers, while the value of the variable 'newLeft' is the reasonable, desired value. The each function I think is collecting the sums of these values and running them for each element x amount of times that the elements found exist (so if there's 5 elements it runs 5 times, I mean.) What I want is this code to execute uniquely for each element, but just once each, not each element 10 times! (that's how many elements are in the html).
So my question is, how can this be achieved? I hope I explained myself well enough, this was tough to iterate. Any help is extremely appreciated. Thank you!
Here's a fun trick: Include += in your .css() call:
$(this).css({left: "+=" + difference});
jQuery does the math for you to get the new value.
Try this:
$(window).resize(function() {
var docWidth = $(window).width();
if (docWidth < 1000) {
var difference = 1000-docWidth;
$('#headNav a,#icons div').each(function(iconInst) {
var left = $("#" + iconInst).position().left;
var newLeft = left - difference;
$("#" + iconInst).css({ 'left' : newLeft });
});
}
});

How do I get an element to scroll into view, using jQuery?

I have an HTML document with images in a grid format using <ul><li><img.... The browser window has both vertical & horizontal scrolling.
Question:
When I click on an image <img>, how then do I get the whole document to scroll to a position where the image I just clicked on is top:20px; left:20px ?
I've had a browse on here for similar posts...although I'm quite new to JavaScript, and want to understand how this is achieved for myself.
There's a DOM method called scrollIntoView, which is supported by all major browsers, that will align an element with the top/left of the viewport (or as close as possible).
$("#myImage")[0].scrollIntoView();
On supported browsers, you can provide options:
$("#myImage")[0].scrollIntoView({
behavior: "smooth", // or "auto" or "instant"
block: "start" // or "end"
});
Alternatively, if all the elements have unique IDs, you can just change the hash property of the location object for back/forward button support:
$(document).delegate("img", function (e) {
if (e.target.id)
window.location.hash = e.target.id;
});
After that, just adjust the scrollTop/scrollLeft properties by -20:
document.body.scrollLeft -= 20;
document.body.scrollTop -= 20;
Since you want to know how it works, I'll explain it step-by-step.
First you want to bind a function as the image's click handler:
$('#someImage').click(function () {
// Code to do scrolling happens here
});
That will apply the click handler to an image with id="someImage". If you want to do this to all images, replace '#someImage' with 'img'.
Now for the actual scrolling code:
Get the image offsets (relative to the document):
var offset = $(this).offset(); // Contains .top and .left
Subtract 20 from top and left:
offset.left -= 20;
offset.top -= 20;
Now animate the scroll-top and scroll-left CSS properties of <body> and <html>:
$('html, body').animate({
scrollTop: offset.top,
scrollLeft: offset.left
});
Simplest solution I have seen
var offset = $("#target-element").offset();
$('html, body').animate({
scrollTop: offset.top,
scrollLeft: offset.left
}, 1000);
Tutorial Here
There are methods to scroll element directly into the view, but if you want to scroll to a point relative from an element, you have to do it manually:
Inside the click handler, get the position of the element relative to the document, subtract 20 and use window.scrollTo:
var pos = $(this).offset();
var top = pos.top - 20;
var left = pos.left - 20;
window.scrollTo((left < 0 ? 0 : left), (top < 0 ? 0 : top));
Have a look at the jQuery.scrollTo plugin. Here's a demo.
This plugin has a lot of options that go beyond what native scrollIntoView offers you. For instance, you can set the scrolling to be smooth, and then set a callback for when the scrolling finishes.
You can also have a look at all the JQuery plugins tagged with "scroll".
Here's a quick jQuery plugin to map the built in browser functionality nicely:
$.fn.ensureVisible = function () { $(this).each(function () { $(this)[0].scrollIntoView(); }); };
...
$('.my-elements').ensureVisible();
After trial and error I came up with this function, works with iframe too.
function bringElIntoView(el) {
var elOffset = el.offset();
var $window = $(window);
var windowScrollBottom = $window.scrollTop() + $window.height();
var scrollToPos = -1;
if (elOffset.top < $window.scrollTop()) // element is hidden in the top
scrollToPos = elOffset.top;
else if (elOffset.top + el.height() > windowScrollBottom) // element is hidden in the bottom
scrollToPos = $window.scrollTop() + (elOffset.top + el.height() - windowScrollBottom);
if (scrollToPos !== -1)
$('html, body').animate({ scrollTop: scrollToPos });
}
My UI has a vertical scrolling list of thumbs within a thumbbar
The goal was to make the current thumb right in the center of the thumbbar.
I started from the approved answer, but found that there were a few tweaks to truly center the current thumb. hope this helps someone else.
markup:
<ul id='thumbbar'>
<li id='thumbbar-123'></li>
<li id='thumbbar-124'></li>
<li id='thumbbar-125'></li>
</ul>
jquery:
// scroll the current thumb bar thumb into view
heightbar = $('#thumbbar').height();
heightthumb = $('#thumbbar-' + pageid).height();
offsetbar = $('#thumbbar').scrollTop();
$('#thumbbar').animate({
scrollTop: offsetthumb.top - heightbar / 2 - offsetbar - 20
});
Just a tip. Works on firefox only
Element.scrollIntoView();
Simple 2 steps for scrolling down to end or bottom.
Step1: get the full height of scrollable(conversation) div.
Step2: apply scrollTop on that scrollable(conversation) div using the value
obtained in step1.
var fullHeight = $('#conversation')[0].scrollHeight;
$('#conversation').scrollTop(fullHeight);
Above steps must be applied for every append on the conversation div.
After trying to find a solution that handled every circumstance (options for animating the scroll, padding around the object once it scrolls into view, works even in obscure circumstances such as in an iframe), I finally ended up writing my own solution to this. Since it seems to work when many other solutions failed, I thought I'd share it:
function scrollIntoViewIfNeeded($target, options) {
var options = options ? options : {},
$win = $($target[0].ownerDocument.defaultView), //get the window object of the $target, don't use "window" because the element could possibly be in a different iframe than the one calling the function
$container = options.$container ? options.$container : $win,
padding = options.padding ? options.padding : 20,
elemTop = $target.offset().top,
elemHeight = $target.outerHeight(),
containerTop = $container.scrollTop(),
//Everything past this point is used only to get the container's visible height, which is needed to do this accurately
containerHeight = $container.outerHeight(),
winTop = $win.scrollTop(),
winBot = winTop + $win.height(),
containerVisibleTop = containerTop < winTop ? winTop : containerTop,
containerVisibleBottom = containerTop + containerHeight > winBot ? winBot : containerTop + containerHeight,
containerVisibleHeight = containerVisibleBottom - containerVisibleTop;
if (elemTop < containerTop) {
//scroll up
if (options.instant) {
$container.scrollTop(elemTop - padding);
} else {
$container.animate({scrollTop: elemTop - padding}, options.animationOptions);
}
} else if (elemTop + elemHeight > containerTop + containerVisibleHeight) {
//scroll down
if (options.instant) {
$container.scrollTop(elemTop + elemHeight - containerVisibleHeight + padding);
} else {
$container.animate({scrollTop: elemTop + elemHeight - containerVisibleHeight + padding}, options.animationOptions);
}
}
}
$target is a jQuery object containing the object you wish to scroll into view if needed.
options (optional) can contain the following options passed in an object:
options.$container - a jQuery object pointing to the containing element of $target (in other words, the element in the dom with the scrollbars). Defaults to the window that contains the $target element and is smart enough to select an iframe window. Remember to include the $ in the property name.
options.padding - the padding in pixels to add above or below the object when it is scrolled into view. This way it is not right against the edge of the window. Defaults to 20.
options.instant - if set to true, jQuery animate will not be used and the scroll will instantly pop to the correct location. Defaults to false.
options.animationOptions - any jQuery options you wish to pass to the jQuery animate function (see http://api.jquery.com/animate/). With this, you can change the duration of the animation or have a callback function executed when the scrolling is complete. This only works if options.instant is set to false. If you need to have an instant animation but with a callback, set options.animationOptions.duration = 0 instead of using options.instant = true.

Categories

Resources