I have various elements that fire the same function. I need to be able to get the closet element of two different types of classes.
Before I would just have a function for each onClick event, though I want them to share the same one and instead just change the current_dragging_element to which ever element was closest to the two types of classes I am looking for.
current_dragging_element = event.target.closest(".draggable_list");
current_dragging_element = event.target.closest(".draggable_item");
async function onMouseDown(event) {
if (HANDLE THE CHANGE IN VAR?) {
current_dragging_element = event.target.closest(".draggable_list");
}
else {
current_dragging_element = event.target.closest(".draggable_item");
}
document.body.classList.add("noselect")
current_dragging_element.style.transform = "rotate(5deg)";
current_dragging_element.style.left = event.clientX - 20
current_dragging_element.style.top = event.clientY - 20
current_dragging_element.classList.add("drag");
current_dragging_element.style.position = "fixed";
current_dragging_element.style.zIndex = 999;
}
This is what I have far, however it doesn't function correctly yet. Any ideas?
Element.closest() works like a CSS selector, just like querySelector(). You can add multiple classes in a single string and get the first encounter of one of those elements.
See example below.
const entries = document.querySelectorAll('.entry');
entries.forEach(entry => {
const closest = entry.closest('.draggable_list, .draggable_item');
console.log(closest);
});
<!-- draggable item is the closest -->
<div class="draggable_list">
<div class="draggable_item">
<div class="entry"></div>
</div>
</div>
<!-- draggable list is the closest -->
<div class="draggable_list">
<div class="entry"></div>
</div>
Related
I am trying to update a c3.js chart using drag and drops with dragula.js, but I don't know how to get the id of the div that is dragged into a new container. My html is something like this:
<div id="collapse1" class="panel-collapse collapse">
<div id="color1" class="form-inline">1</div>
<div id="color2" class="form-inline">2</div>
<div id="color3" class="form-inline">3</div>
</div>
<div id="collapse2" class="panel-collapse collapse">
</div>
and I'm using dragula.js to drag and drop:
dragula([collapse1,collapse2]);
I am really new to jquery, but following this question, to access the id of the <div> dropped into collapse2 in I was trying to do something like this:
alert($("#collapse1.collapse2 div:first").attr("id"));
But no results. Any help would be really appreciated
Dragula has three Elements One is Source Div, Target Div and Its associated Element. Following Method Works For Me as Charm except i am Not using get() method which has version issue.
You Can Try Both.
Dragula gives you the id of dropped div, Source Div, Target Div.
const dragula = Dragula(['', '']);
dragula.on('drop', (el, target, source, sibling) => {
const elementId = $(el).attr("id");
const targetID = $(target).attr("id");
const sourceId = $(source).attr("id");
}
Can't answer the question directly because I am not familiar with dragula. However, I have used jqueryUI drag drop extensively and its a really good tool. You might want to give that framework a try.
Since you asked for an example, I dug into some of my old code. You might want to go look through the jqueryUI draggable and droppable tutorials to give you some background before looking at this. I have included parts of a function. I put little dots to show you where code has been left out. I have put <<< next the key lines for you. Notice how I use closure to make references available across different parts. Closure is soooo awesome. I abuse the death out of it, so learn how to use it if you can.
Note that once I got my drag object, that is what you are asking for. Notice how I reference the variable to my function later when I register the draggable.
Btw, notice there is also a stop drag function referenced which I don't show the definition of. If you move the declaration of the dragObject outside of startDrag then you can also see it from stopDrag since the definition of the function is "enclosed" in the outside register function.
function tapeChart_registerDraggables(parentObject,scope) {
if ((parentObject==null)||(parentObject==undefined)) {
parentObject=$jq(document.body);
}
var availablesShow = false;
var savingToServer = false;
var dragClone = null;
var startDrag = function(event, ui) {
tapeChartDraggingReservation = true;
var dragObject = event.target; <<<<<<
if (dragObject.getAttribute("unassigned")=="true") {
var is_chrome = window.chrome;
var is_safari = navigator.userAgent.toLowerCase().indexOf('safari/') > -1;
if (!is_chrome && !is_safari) {
$(ui.helper).css("margin-left", event.clientX - $(dragObject).offset().left);
$(ui.helper).css("margin-top", event.clientY - $(dragObject).offset().top);
}
}
...
// assigned rooms
if (scope!="UNBLOCKED") {
// register items in the grid
$(parentObject).find( ".NODRAGHELPER" ).draggable(
{
snap : "true",
revert : "invalid",
start: startDrag, <<<<
stop: stopDrag
}
)
.click(function(){
if ( $(this).is('.NODRAGHELPER-dragging') ) {
return;
}
// seems that the user can drop and click fast
// prevent this
if (!savingToServer) {
tapeChart_getReservation(this);
}
return false;
});
}
...
I'm looking to address a performance issue I'm having with a very large DOM. In essence, this a word-processing style app inside the browser using contenteditable divs.
Suppose I have a structure like this:
<div class="header">...</div>
<div class="actor">...</div>
<div class="director">...</div>
<div class="producer">...</div>
<div class="writer">...</div>
<div class="executive">...</div>
<div class="studio">...</div>
<div class="footer">...</div>
I then have some code which ends up returning (for example):
<div class="writer">...</div>
as a jQuery object. I then need to retrieve all of this object's surrounding divs between header and footer as a selection and then further filter this list using a class e.g. 'actor'.
Currently, I have the following code, which works correctly:
// Find header
var header_object = object.prevUntil(".header").last().prev();
// Select all objects between header and footer, and then filter
var object_list = header_object.nextUntil(".footer", ".actor");
// Iterate through object_list
object_list.each(function()
{
// Run additional code on the objects
});
The only problem is that due to the app being a word processor of sorts, the DOM structure is often very large (e.g. over 5000 elements) and executing this code locks up the browser for an unacceptable amount of time (over 10 - 30 seconds).
As such, I'm looking for a way to customize the code I have to make it more efficient / improve performance.
I should also point out that the HTML structure above is not (header - 5000 elements - footer), rather it is 200 x (header - elements - footer). As such, each traversal operation is only maybe 25 elements from header to footer, but it has to run many times.
Any suggestions? Many thanks!
You could enhance performance by not using jQuery, and creating your own functions that are more specific to your use case.
function getClosest(el, klass, dir) {
while (el && (!el.classList.contains(klass))) {
el = el[dir ? 'previousElementSibling' : 'nextElementSibling'];
}
return el;
}
function getbetween(from, to, filterKlass) {
var list = [];
while(from && to && from !== to) {
if ((from = from.nextElementSibling) !== to) {
filterKlass ? (from.classList.contains(filterKlass) ? list.push(from) : Infinity) : list.push(from);
}
}
return list;
}
var object = $('.writer');
var element = object.get(0);
var header_object = getClosest(element, 'header', true);
var footer_object = getClosest(element, 'footer', false);
var object_list = getbetween(header_object, footer_object, 'actor');
object_list.forEach(function(element) {
console.log(element);
});
FIDDLE
Traversing the next element sibling directly, and checking for classes, should be much faster than using nextUntil
(firstly I recommend seeing the related image)
I have 3 container having same class .container. Also, user can add child divs dynamically into the containers. The user will start adding divs (that is .parent) by clicking '.add' div inside each '.parent'.
The containers can have at max 3 div. If the user has added 3 divs already then the next div should go in the second container and so on. Once the last container(the third one) is full, an alert should pop up saying "You cannot add anymore divs."
I have two questions:
Using jquery how can I limit the number of '.parent' divs per container to 3. If the user tries to add another it is added to container 2 (unless container 2 has 3 child divs, then it would go to container 3)?
Once the container of page 3 is full (3 divs) an alert should pop up saying "You cannot add anymore divs".
The only snippet of code that I have is not working. Please help me with the code. I am novice in all this stuff.
Thanks in advance.
Related image: [1]: http://i.stack.imgur.com/zi78d.png
Sample code:
<html>// the containers
<div class="container"></div>
<div class="container"></div>
<div class="container"></div>//divs that are supposed to be appended
<div class="parent">
<div class="add"></div>
</div>
<div class="parent">
<div class="add"></div>
</div>
<div class="parent">
<div class="add"></div>
</div>
<div class="parent">
<div class="add"></div>
</div>. . .
</html>
.
<script>
var $pages = $('.container');
var child = '$('.add ').parent()';
$(".add").on('click', function () {
var childAdded = false;
$pages.each(function () {
var $container = $(this);
if ($container.children().length < 3) {
$container.append.('child');
childAdded = true;
return false;
}
});
if (!childAdded) {
alert("You can not add any more divs");
}
});
</script>
Several problems in your code
You want the instance of the parent class when you click on the button, not all .parent
You have syntax errors using quotes around jQuery objects that shouldn't be there.
Here's a simple approach using filter() method.
$(".add").on('click', function () {
/* filter $pages down to first available one with space */
var $page=$pages.filter(function(){
return $(this).children().length < 3;
}).first();
if( !$page.length ){ /* if no elements returned from filter, they are all full */
alert("You can not add any more divs");
}else{
/* get instance of parent based on button that was clicked which is "this" */
var $parent=$(this).closest('.parent');
$page.append( $parent );
}
});
DEMO
filter() API docs
You have to track the amount of divs you have added yourself. Then, use this information to determine which .container you should put it in. Something like this:
var added = 0;
...
$(".add").on('click', function () {
var target;
if(added<3) {
target = $pages[0];
} else if (added<6) {
target = $pages[1];
} else if (added<9) {
target = $pages[2];
} else {
alert("You can not add any more divs");
return
}
$(target).append($(this).parent());
added += 1;
I'm trying to figure out the following.
I have following jQuery code:
var as = "";
var bPlay = 0;
audiojs.events.ready(function() {
as = audiojs.createAll();
$(".audiojs .play-pause").click(function() {
var e = $(this).parents(".audiojs").index(".audiojs");
$.each(as, function(t, n) {
if (t != e && as[t].playing) {
as[t].pause()
}
})
bPlay = !bPlay;
if (bPlay == 1) {
$(".bar").each(function(i) {
fluctuate($(this));
});
} else {
$(".bar").stop();
}
})
});
In a nutshell it preforms list of things when someone clicks particular .audiojs instance on a page. 1) checks if there is any other instance playing, if there is pauses it. And if it is playing applies fluctuate function to elements on a page that have class="bar". This is the issue! I don't want to apply it to all .bar's on a page, but only to a specific group that is associated with particular .audiojs instance (the one that is being clicked and is playing).
I thought of the following solution. Each .audiojs instance is inside a div tag that has id like "post-1", "post-2" etc.. where numerical value is post id from database. I can add this numerical id to bar, so it would be like bar-1, bar-2 etc... However after this I'm having issues.
For javascript to work I need to retrieve numerical value from "post-[id]" associated with audiojs instance that is being clicked and than store it somehow, so I can use it like this afterwards
bPlay = !bPlay;
if (bPlay == 1) {
$(".bar-[value retrieved from post-...]").each(function(i) {
fluctuate($(this));
});
} else {
$(".bar-[value retrieved from post...]").stop();
}
Could someone explain to me how it can be achieved?
Honestly, the easiest way would be to stick it in a custom data-* attribute on the <div id="post-X"> element, like so:
<div id="post-1" data-bar="bar-1">...</div>
Then, you said your .audiojs element is inside that <div>, so just go from this inside the event handler to that <div> element (using .closest()) and get the value of it:
var barId = $(this).closest('[id^="post-"]').attr('data-bar');
Then when you need to use it:
$("." + barId).each(function(i) {
fluctuate($(this));
});
Instead of embedding the value in a class or ID, use a data-* attribute:
<div class="audiojs" data-fluctuate-target="bar-1">
<button type="button" class="play-pause">
<!-- ... -->
</button>
</div>
<div class="bar-1">
<!-- ... -->
</div>
In your click event handler, use the following to fluctuate or stop the correct elements:
var fluctuateClass = $(this).closest('.audiojs').attr('data-fluctuate-target');
$('.' + fluctuateClass).each(function () {
if (bPlay == 1) {
fluctuate($(this));
} else {
$(this).stop();
}
});
I read the question Scroll to a particular element w/ jQuery and in the question, the original HTML code already has ids to the paragraph elements. However, in my case, I generate the element (list element) dynamically and create ID on runtime. How would I scroll to that particular element with or without jQuery?
To give more detail about my problem:
I am creating a phonegap project to get the list of contacts in the iPhone and display a scrolling list (I use the iscroll plugin) in a div. I categorize the first names A-E, F-J, K-O, P-T, U-Z and group them. If the user touches F-J on the side (as you find in iPhone contacts app), the div should scroll to the first contact that belongs to group F-J..
<div id ="tablediv">
<div id="scroller"></div>
</div>
<div id="sorter">
<span id="gr1">A-E</span>
<span id="gr2">F-J</span>
</div>
Javascript:
var g1 = ['a','b','c','d','e']; //contact's first name starting with these chars
var g2 = ['f','g','h','i','j']; //contact's first name starting with these chars
var idg1=null, idg2=null; // first id of the contact which was in g1, g2
//Array is an array of objects
//example: array = [ {'fname':'x','lname':'y','number':'123'},{..},{..}];
function generateTable(array) {
gpDiv = document.getElementById("scroller");
pDiv = document.createElement("ul");
pDiv.id = "thelist";
for(var i=0;i<array.length;i++) {
cDiv = document.createElement("li");
cDiv.id = 'cd'+i; //id created dynamically
cDiv.textContent = array[i].fname+"\u000a"+array[i].lname;
var ch0 = array[i].fname[0].toLowerCase();
if($.inArray(ch0,g1)!=-1 && idg1==null) {
idg1 = cDiv.id;
document.getElementById('gr1').addEventListener('click',function(){goToG1(idg1);},false);
}
if($.inArray(ch0,g2)!=-1 && idg2==null) {
idg2 = cDiv.id;
document.getElementById('gr2').addEventListener('click',function(){goToG2(idg2);},false);
}
pDiv.appendChild(cDiv);
}
gpDiv.appendChild(pDiv);
}
function goToG1(id) {
$('#tablediv').scrollTop($('#'+id).offset().top);
}
function goToG2(id) {
$('#tablediv').scrollTop($('#'+id).offset().top);
}
The above code doesn't work, as I think since the ids are allotted at runtime, I am not able to scroll to that particular element. Please help
Hmmm, All I needed to do was this.
function goToG1(id) {
document.getElementById(id).scrollIntoView();
}
It appears to me that the ids still work even though they are allotted at run time.
You code worked more or less - you were however using code:
$("#tablediv").scrollTop(...)
Instead of
$(document).scrollTop(...)
Other than that it works - see here: http://jsbin.com/umatuj/2/edit