Empty list message on jquery-sortable - javascript

I'm working with jquery-sortable and I'm having some difficulty modifying the list container (ul) when it's been emptied or loaded empty. For example, If you have two containers:
A collection list to drag from that always contains a few items.
A destination list which loads empty (unless it is being edited and it will contain some list items but can be emptied by dragging them out of there
The empty container (ul) should display a message (i.e. nothing here) whenever it loads empty or it gets emptied on edit.
I tried several approaches with no avail.
SAMPLE HTML FOR EMPTY CONTAINER
<ul id="mediaItemsListDest" class="playlist-items connectedSortable">
<!-- force a message in html -->
<p>Drag and drop an item from the list</p>
</ul>
DIFFERENT JQUERY APPROACHES
if( $("#mediaItemsListDest li").length >= 1 ) {
//remove it when the ul contains an li
$("#mediaItemsListDest p").css('display','none');
}
or
if( $("#mediaItemsListDest li").length === 0 ) {
//no li is found, display a message via jquery but don't add it as a <p> element in the html
$(this).html("Sorry, this is empty");
}
or
if( !$("#mediaItemsListDest").has("li").length ) {
$(this).html("Sorry, this is empty");
}
None of them worked. How else can I hijack this empty or emptied list?
Here's a testing fiddle for you:
DEMO
Thanks in advance.

You need to handle on every list change the error message state so let's say we have the following HTML - example from your demo:
<ol id="mediaItemsListDest" class="simple_with_animation vertical">
<p>Drag and drop an item from the list</p>
<li>Item 1</li>
<li>Item 2</li>
</ol>
Additionally I have extended with a function which is handling the message state, code is placed on initialization part of the application:
function handleMessage() {
let liObjects = $('#mediaItemsListDest li');
let message = $('#mediaItemsListDest p');
console.log('length', liObjects.length);
console.log('message', message);
if (liObjects.length !== 0) {
message.css('display', 'none');
} else {
message.css('display', 'inline');
}
}
handleMessage();
This function needs to be called in onDrop event:
onDrop: function ($item, container, _super) {
// code part removed but you can find in your demo
handleMessage();
}
I did a quick test and it was working fine. I hope this one is helping, let me know if you need more information.

Related

How to Iterate through an Unordered List to get a console.log of the innerText of the LI element

I need to create an event-listener which fires when a user clicks one of the list items in the HTML.The action should call a function named listItemText which returns the innerText of the list item which was clicked--ie: if they click the first li it should log "Walk the dog"
I've tried everything I can think of to get the correct corresponding innerText of the li item that is being clicked on. At best, I've gotten either the whole list back in the console.log, or the last element of the list.
I've tried so many things at this point it would be impossible to recall. More or less the code below is a variant of what I've attempted
<ul id="todo-app">
<li class="item">Walk the dog</li>
<li class="item">Pay bills</li>
<li class="item">Make dinner</li>
<li class="item">Code for one hour</li>
</ul>
var targetUl = document.getElementById('todo-app').childNodes;
this.addEventListener('click', listItemText);
function listItemText(event) {
var liClicked = event.target;
for (i=0; i<targetUl.length; i++) {
if (liClicked == 'LI') {
console.log(targetUl[i].innerText)
}
}
}
I expect to get the text content of the li tag but I keep getting undefined at this point. Help would be greatly appreciated.
If I understand correctly you want the console.log of the text of the li element you click, so I suppose you can try this code below:
var targetUl = document.getElementById('todo-app').addEventListener("click",listItemText);
function listItemText(event) {
var liClicked = event.target;
if(liClicked && liClicked.nodeName == "LI"){
console.log(liClicked.textContent)
}
}
<ul id="todo-app">
<li class="item">Walk the dog</li>
<li class="item">Pay bills</li>
<li class="item">Make dinner</li>
<li class="item">Code for one hour</li>
</ul>
This is the principle of event delegation, where you don't need to attach event listeners on all of the elements but only to the parent node, if the event happened it will bubble up to the parent and using the event.target you can get the reference of the child element which was clicked.
I just now ended up figuring it out. Did it slightly differently, but ultimately the same idea. Was having a hard time understanding event.target and how it works. Here's my answer--which is almost the same as your answer, good sir:
var toDoList = document.getElementById("todo-app");
toDoList.addEventListener('click', listItemText);
function listItemText(e) {
console.log(e.target.innerText);
return e.target.innerText;
}

arrow keys navigation through li (no jquery)

I'm trying to figure out a way to create an extremely basic autocomplete without 3rd parties dependencies. so far I've gotten to populate a list of results with an ajax call, and with mouse onclick events on each li the script completes the fields as supposed.
what I need to implement is an up/down/enter keys navigation system based on pure js, and after hours spent searching I gave up. this fiddle explains quite perfectly my situation, with the difference that it does require jQuery.
I'd rather not paste any of my own code here as the final aim is learning the process, but since I'm linking to jsfiddle I'm required to so here's the fiddle.
fiddle HTML:
<div id="MainMenu">
<ul>
<li class="active">PATIENT TEST</li>
<li>QC TEST</li>
<li>REVIEW RESULTS</li>
<li>OTHER</li>
</ul>
</div>
Up
Down
fiddle JS:
$(document).ready(function () {
$('#btnDown').click(function () {
var $current = $('#MainMenu ul li.active');
if ($current.next().length > 0) {
$('#MainMenu ul li').removeClass('active');
$current.next().addClass('active');
}
});
$('#btnUp').click(function () {
var $current = $('#MainMenu ul li.active');
if ($current.prev().length > 0) {
$('#MainMenu ul li').removeClass('active');
$current.prev().addClass('active');
}
});
$(window).keyup(function (e) {
var $current = $('#MainMenu ul li.active');
var $next;
if (e.keyCode == 38) {
$next = $current.prev();
} else if (e.keyCode == 40) {
$next = $current.next();
}
if ($next.length > 0) {
$('#MainMenu ul li').removeClass('active');
$next.addClass('active');
}
});
});
thanks a lot in advance to anyone willing to point me in the right direction.
This turned out to be simpler than I expected, and I've came up with the following code which appearently does the job quite well.
Things to take in account are:
the HTML attribute 'tabindex' must be specified on each element for the .focus() to be applied
to have a ENTER->submit feeling, you MUST target a link element within the li (still, I'm achieving this with onclick events not included here)
this works with an extremely simple list structure, so far I haven't tested it with nested dropdown menus
Note: this is most likely not suitable for a copy/paste situation, but as far as I can tell this method is procedurally currect, and can get you started developing more complex solutions
This is the basic HTML:
<input type="text" name="main_input" id="input" />
<ul id="list">
<li class="listElement">li content</li>
<li class="listElement">li content</li>
<li class="listElement">li content</li>
</ul>
And here's the JS function, triggered when the list above is populated and shown:
function scrollList() {
var list = document.getElementById('list'); // targets the <ul>
var first = list.firstChild; // targets the first <li>
var maininput = document.getElementById('input'); // targets the input, which triggers the functions populating the list
document.onkeydown = function(e) { // listen to keyboard events
switch (e.keyCode) {
case 38: // if the UP key is pressed
if (document.activeElement == (maininput || first)) { break; } // stop the script if the focus is on the input or first element
else { document.activeElement.parentNode.previousSibling.firstChild.focus(); } // select the element before the current, and focus it
break;
case 40: // if the DOWN key is pressed
if (document.activeElement == maininput) { first.firstChild.focus(); } // if the currently focused element is the main input --> focus the first <li>
else { document.activeElement.parentNode.nextSibling.firstChild.focus(); } // target the currently focused element -> <a>, go up a node -> <li>, select the next node, go down a node and focus it
break;
}
}
}
Apologies in advance for the kinda chaotic layout of the code, the function I came up with is a bit more complex and I've stripped out most of it for explaination purposes.
Needless to say, I'm looking forward any comment about the solution above, in regard of errors, improvements or known compatibility issues.
you wrote
tabindex="1"
Why not just tabindex="0"?
Using a positive umber greater than 0 is only needed if you want to cahnge the default order of navigation.

How to find previously dropped value from droppable div

I am working on dynamic formula generator using jquery drag and drop functionlaity
What i have done so far is
I have two 2 list
<ul id="head">
<li class="horizontal">Salary</li>
<li class="horizontal">Workers</li>
<li class="horizontal">Perday</li>
</ul>
and
<ul id="operations">
<li class="horizontal">Add +</li>
<li class="horizontal">Sub -</li>
<li class="horizontal">Multiply *</li>
<li class="horizontal">Divide /</li>
</ul>
The process is user will first drag from first list and drop in droppable div and they will add anything from operations list like (+,- etc..).
The Jquery i have used :
$('#head li').draggable({
cursor: 'move',
helper: 'clone',
connectToSortable: "#drop",
});
$("#drop").droppable({
drop: function (event, ui) {
$(this)
.removeClass("ui-droppable ui-sortable")
.addClass("horizontal ui-draggable ui-draggable-handle")
}
}).sortable();
droppable div
<div id="drop" style="border:2px solid;min-height:100px;"></div>
My Problem is that i have to validate user should not drop more than one operations at a time. ex:Multiply * Add + this is wrong. The operations must come between 2 text. so how i can we find the previously added value in this droppable div (drop).
Thanks in advance
How about using some data attribiutes to id your operations and a variable in js to store what was dropped last, like this:
HTML
<div class='js-droppable' data-drop-type='op'>operation 1</div>
JS
var lastDropped = '';
var processDroppable = function () {
var dropped = $(this).data('drop-type');
if (lastDropped == 'op' && dropped == 'op') {
console.log('not allowed')
} else {
console.log('you may drop ...');
}
lastDropped = dropped;
}
$('.js-droppable').click(processDroppable);
The data attribute stores the type of droppable and then is used to catch any duplicate droppable that is not allowed.
You could do any logic really based on identifying your droppables, but lastDropped == 'op' && dropped == 'op' covers the case in the question.
The working example is here in JSFiddle, I've used click events rather than dropped events, for simplicity; open the console to see the output.

Trouble formatting .join() separators

Working on a twitter bootstrap wizard page and outputting selected check-box values to next tab-pane. Because there can be multiple selections (ie. :checked), I'm using map function and join(). I'm trying to wrap each output in a <li> but the first item does not get wrapped -- only every item after the 1st.
Eg.
Checkbox 1
<li>Checkbox 2</li>
<li>Checkbox 3</li>
Here is my code:
$(document).ready(function() {
$('#rootwizard').bootstrapWizard({
onNext: function(tab, navigation, index) {
if (index == 1) {
if (!$('.selection:checked').val()) {
alert('Please select a solution to try');
$('.selection').focus();
return false;
}
}
$('#showSelection').html($('.selection:checked').map(function() {
return $(this).val();
}).get().join('<li>'));
}
});
Not sure if I should be using a different method? Or... if there is a way to pre-append and force a separator "wrap" around all selections. Any insight/clarity would be appreciated.
Try doing it like this:
$('#showSelection').html('<li>' + $('.selection:checked').map(function() {
return $(this).val();
}).get().join('</li><li>') + '</li>);
This is because the separator for the join will only be added in between the elements, not before and after them so your first element will not have an opening li tag and the last will not have a closing li tag.

jquery - on click of menu item or another element will change body background image

I've read many tutorials and can't seem to get it right. Ok I know that the jquery click function works when you are doing something to the exact same element but how do you make it effect another and toggle back?
I have a menu and when I click on an item I want the background (body) to change to an image.
Example:
HTML
<body>
<ul class="menu">
<li class="menu-item"><a>item 1</a></li>
<li class="menu-item"><a>item 2</a></li>
<li class="menu-item"><a>item 3</a></li>
</ul>
</body>
JQUERY
$(".menu-item a").click(function () {
$(body).css('background', 'http://example.com/image.png'); <-- first menu item
$(body).css('background', 'http://example.com/image.png'); <-- second menu item
$(body).css('background', 'http://example.com/image.png'); <-- third menu item
});
You can use .index() - DEMO
$("a").on("click", function(e) {
e.preventDefault();
var i = $("li").index( $(this).parent() );
if ( i === 1 ) {
$('body').css('background', 'beige');
} else if ( i === 2 ) {
$('body').css('background', 'honeydew');
} else {
$('body').css('background', 'pink');
}
});
Does this seem about like what you're trying to do?
$(".menu-item a:nth-child(1)").click(function () { // first menu item
$(body).css('background', 'http://example.com/image.png');
});
$(".menu-item a:nth-child(2)").click(function () { // second menu item
$(body).css('background', 'http://example.com/image.png');
});
$(".menu-item a:nth-child(3)").click(function () { // third menu item
$(body).css('background', 'http://example.com/image.png');
});
I don't know what you are trying but I could give you hints.
$(".menu-item a") // is an array/jquery collection of all elements that match the selector
.click(function () { // binds the function now to the click event of every item found
$(this); // is now the currently clicked element
// you can now traversal with $(this) for example
$(this).siblings(); // will be a collection of all surrounding elements
$(this).next(); // is the next element after the current one, can be chained like:
$(this).next().next(); // but I wouldn't recomand
$(this).prev(); // same as next but prev
$(this).parent(); // selects the direct parent element, in your case the li element
$(this).children(); // would select the direct children of the current element
// and so on.... there are much more possibilities
// on every of this possibilities you can do your background change
$("some selector"); // is of course still possible
// i think you are trying to do this:
var menuItems = $(".menu-item a");
menuItems.eq(0).css("background", "url to bg 1");
menuItems.eq(1).css("background", "url to bg 2");
menuItems.eq(2).css("background", "url to bg 3");
})
Look at the Traversing section of the jQuery docu. I would also always recommend to look what jQuery is actually doing. Many people hide behind jQuerys api and have no idea whats happening. This results into many misunderstandings.
You may try something like this
HTML:
<body>
<ul class="menu">
<li class="menu-item"><a name="blue" href="#">item 1</a></li>
<li class="menu-item"><a name="red" href="#">item 2</a></li>
<li class="menu-item"><a name="orange" href="#">item 3</a></li>
</ul>
</body>
CSS:
.red {
background:red;
}
.blue {
background:blue;
}
.orange {
background:orange;
}
Jquery:
$('.menu').on('click', 'a', function () {
var bgColor = $(this).attr('name');
$('body').removeClass().addClass(bgColor);
return false;
});
DEMO
The way I suggest going about this is to grab the position of the element that has been clicked. You can do this by using jQuery's index() function as other posters have suggested. Remember this will give you a zero-based index of the position which means that the position counting starts at 0 as opposed to 1.
Based on the item that has been clicked, you assign a CSS class to the target item which is the body element based on the sample code you provided.
Also, I noticed that your JS comments are still invalid even they were edited. Single line comments in JS use a double forward slash, // whereas multiline comments begin with /* and are closed by */.
This is my solution: http://jsfiddle.net/tgZUK/.

Categories