I'm attempting to build my first jquery plugin. It's a simple drag-and-drop game. Basically the plugin does this:
Identify a group of child divs under the parent div
apply some styling to those child divs and randomize their
order. apply a .draggable function to each child div.
identify the target div(s) and add .droppable function(s) to them. When a draggable is placed on a droppable, make sure it's the correct draggable for that droppable.
after all the draggables have been placed on the correct
droppables, let the player know they've finished the game.
what I need to do now is let the page know that #6 has happened, so that if I wanted to animate something or fire off another function, it will wait until that point.
Unfortunately, I can't figure out what exactly it is that I need to learn. Would what I'm after be an event listener? Something else? I've got a small if statement for #6:
if (score === dragsTotal) {
$(dragsCont).append("<div style='font-weight:bold;text-align:center'>" + settings.finishText + "</div>");
}
so I'm sure that's where, whatever-it-is-that-i-need-to-learn has to go. I thought that all I might need is a boolean, but I don't know how to let the page know what that boolean is or that it's changed.
If it's needed, the codepen here: http://codepen.io/kking/pen/YqXGaw/
I'd hook in to the drop event. Each time they drop one, check them all to see if they're finished yet.
Related
HTML has scrollable elements. At any given time one of them is going to receive keyboard scrolling controls like up/down arrow, pageup/pagedown etc.
How can I find out which element is currently active in this way, and how can I make an element active in this way?
Here's jsfiddle to demonstrate the issue.
I can click on e1, e2, e3, or e4. If I do, then arrow keys will scroll that element.
The obvious way to do this would be to call element.focus(). But that does nothing when called on a scrollable div, and document.activeElement keeps pointing to body not to any of them (the one that's actually active, or the one I tried to make active).
So:
is there some other DOM thing which controls it?
or is this functionality not exposed by browsers in any way?
or do I need to set a bunch of tabindex settings to access this state through JS, even though browsers can handle active scrolling status without any tabindex by theselves?
Use Event Listeners To Do So
function gg1(){
document.getElementById("focused").innerText = "e1"
}
function gg2(){
document.getElementById("focused").innerText = "e2"
}
document.getElementById("e1").addEventListener("scroll", gg1);
document.getElementById("e2").addEventListener("scroll",gg2);
This Code Is Just The Basic Version Of How You could achieve it. You can always minify it.
I am trying to get some contenteditable elements working. Obviously this is incredibly easy with just a simple HTML5 attribute, but I want to be able to toggle the amount of elements with the attribute and also toggle the attribute itself. For example, my starting element is this <article class="column contentEditable"> and then the class of contentEditable is used to toggle the contenteditable attribute. But because I am duplicating this element an amount of times based on what the user selects - I need to run the .focus() action as a function which is then called when some more articles are added. With me so far? Hope so.
Here is the jQuery I have so far (bearing in mind another function sets the class contentEditable to have the attribute contenteditable)
// content edittable
function makeEditable(action){
$('.contentEditable').focus(function(){
$(this).addClass('active');
$(this).prepend('<div class="toolbar" contenteditable="false">TEST</div>');
});
$('.contentEditable').blur(function(){
$(this).removeClass('active');
$(this).remove('.toolbar');
alert('test');
});
}
To a certain extent this works perfectly, however because an article may have already been there when this DOM call was issued before, it means that it's running twice or more (depending on how many times I've changed the option in the select. How on earth can I get the function to only run once per item, i.e. not stack.
Hope this all makes sense, pretty difficult to explain.
I think what's happening here is that you are calling the makeEditable function everytime the user is selecting an element to edit (if I understand your workflow right). If that's happening you're adding a new focus and blur event every time they do and that will cause the event to fire multiple times.
What I recommend is something like this:
function addElement(containerElement) {
// containerElement is a string with the jQuery selector of the parent element
var el = $('<article class="column contentEditable"></article>').appendTo(containerElement);
el.focus(function(){
$(this).addClass('active');
$(this).prepend('<div class="toolbar" contenteditable="false">TEST</div>');
});
el.blur(function(){
$(this).removeClass('active');
$(this).remove('.toolbar');
alert('test');
});
}
function toggleEditable(el) {
// here el is a string with the jQuery selector of the element
$(el).toggleClass('contentEditable');
}
It was a simple case of having a process class on it to only run it once, each time the .focus is run it removes the process class. Pretty simple really, fresh eyes helped after a break. Thanks.
When a draggable attribute is enabled on a parent element(<li>) I cant make contenteditable work on its child element (<a>).
The focus goes on to child element (<a>),but I cant edit it at all.
Please check this sample
http://jsfiddle.net/pavank/4rdpV/11/
EDIT: I can edit content when I disable draggable on <li>
I came across the same problem today, and found a solution [using jQuery]
$('body').delegate('[contenteditable=true]','focus',function(){
$(this).parents('[draggable=true]')
.attr('data-draggableDisabled',1)
.removeAttr('draggable');
$(this).blur(function(){
$(this).parents('[data-draggableDisabled="1"]')
.attr('draggable','true')
.removeAttr('data-draggableDisabled');
});
});
$('body') can be replaced by anything more specific.
If new contenteditable elements are not added in the runtime, one can use bind instead of delegate.
It makes sense that the draggable and contenteditable properties would collide. contenteditable elements, like any text field, will focus on mousedown (not click). draggable elements operate based on mousemove, but so does selecting text in a contenteditable element, so how would the browser determine whether you are trying to drag the element or select text? Since the properties can't coexist on the same element, it appears that you need a javascript solution.
Try adding these two attributes to your anchor tag:
onfocus="this.parentNode.draggable = false;"
onblur="this.parentNode.draggable = true;"
That works for me if I add it to the <a> tags in your jsFiddle. You could also use jQuery if it's more complicated than getting the parentNode.
Note: This is a workaround since I believe the inability for these two functionalities to work together resides in the HTML spec itself (i.e. the not working together thing is intentional since the browser can't determine whether you want to focus or drag on the mousedown event)
I noticed you explicitly set 'no libraries', so I will provide a raw javascript/HTML5 answer
http://jsfiddle.net/4rdpV/26/
This was my crack at it.
First of all, it might be better to include the data in one single localStorage item, rather than scatter it.
storage={
'1.text':'test 1',
'2.text':'test 2'
}
if(localStorage['test']){
storage=JSON.parse(localStorage['test'])
}
this creates that ability, using JSON to convert between object and string. Objects can indeed be nested
I also added (edit) links next to the items, when clicked, these links will transform the items into input elements, so you can edit the text. After hitting enter, it transforms it back and saves the data. At the same time, the list items remain draggable.
After saving, hit F12 in chrome, find the console, and look in the localStorage object, you will see all the data was saved in localStorage['test'] as an Object using JSON.stringify()
I tried my best to design this to be scaleable, and I think I succeeded well enough; you just need to replace the HTML with a container and use a javascript for loop to write out several items, using the iterator of your choice to fill the parameter for edit(). For example:
Say you changed storage to hold "paradigms" of lists, and you have one called "shopping list". And say the storage object looks something like this:
{
"shopping list":{
1:"Milk",
2:"Eggs",
3:"Bread"
}
}
This could render that list out:
for(i in storage['shopping list']){
_item = storage['shopping list'][i];
container.innerHTML+='<li draggable=true><a id="item'+i+'">'+_item+'</a> (edit)</li>'
}
Of course, if you were to edit the structure of the storage object, you would need to edit the functions as well.
The output would look something like this:
Milk (edit)
Eggs (edit)
Bread (edit)
Don't worry about the input elements if that worries you; CSS can easily fix it to look like it didn't just change.
If you don't want the (edit) links to be visible, for example, you can do this in CSS:
a[href="#"]{
display:none;
}
li[draggable="true"]:hover a[href="#"]{
display:inline;
}
Now the edit links will only appear when you hover the mouse over the list item, like this version:
http://jsfiddle.net/4rdpV/27/
I hope this answer helped.
Using html5sortable and newer JQuery events (delegate is deprecated, answer 3 years after initial question), bug still affects Chrome 37. Contenteditable spans and html5sortable seem to play nice in other browsers. I know this is only partially relevant, just keeping documentation on changes I've noticed.
$(document).on('focus', 'li span[contenteditable]', function() {
$(this).parent().parent().sortable('destroy'); // removes sortable from the whole parent UL
});
$(document).on('blur', 'li span[contenteditable]', function() {
$(this).parent().parent().sortable({ connectWith: '.sortable' }); // re-adds sortable to the parent UL
});
I've got the following problem:
I have a set of LI elements that have to go from one state to another (two separate css classes) with a smooth transition. To do this, I'm using JQueryUI's Effect API (switchClass)
for every LI element, I've hooked two JQuery listeners: mouseover and mouseout, which change the state correspondingly. Clear enough. Now, I'm not of a JQuery expert, so I must be missing something pretty standard, but every time when I move the mouse out of the LI element BEFORE the transition has finished, the transition just kinda hangs midway, and the LI elem becomes irresponsive to further listening.
Please, help.
You can use .stop([clear queue],[jump to end]) to end the animation before the next one is called. Substitute true/false based on if you want to do that in your stop statement. Add it before the call, ie $('element').stop(true,true).animate({....
http://api.jquery.com/stop/
I'm playing with Drag and drop funcitonality for the first time so I'm not entirely sure what I'm doing!
I need to add a class to a "portlet" while it is being dragged. I don't want to use the clone functionality because I want the user to drag the actual element, I just want to nodify the element while it is being dragged and reset it when it's dropped.
Can anybody help?
Perhaps there's some sort of a 'beforedrag' event you can bind to? It would be easier to add the class to an element before the user actually starts dragging it, rather than during.
If you're using jQuery UI, there's a 'start' event on draggable you can use:
http://docs.jquery.com/UI/Draggable#events
Also, you can use the "helper" option like this:
helper : function(ev, el) {
return ($(el).clone().addClass("beingDragged"));
}
Should your portlets become in the future too heavyweight to drag, you could use that to build a simplified version while dragging to smooth things out :)