I keep getting the error cannot read property left of undefined when I drag the sortable jQuery UI elements around. Everything is working like I intend but I keep getting the error and would like to fix the problem causing it.
The intent is to have a sortable list with multi-select capability so that you are not forced to drag one element at a time. Like I said that is working and I followed the code on this fiddle
The strange thing is when I created a codepen to include with this question I am not getting the error message in the console anymore. I tried codepen and jsfiddle. I realize it may be difficult to troubleshoot if you can not see the error for yourself but am hoping someone might be able to spot a mistake or give me suggestions on what to check.
Here is my codepen and some code:
EDIT:
The problem has been solved thanks to a comment. I needed to change my jQuery version.
HTML:
<div id="wrapper">
<ul id="elePool" class="sortme">
<li class="draggable element" data-element="1">sku</li>
<li class="draggable element" data-element="2">type</li>
<li class="draggable element" data-element="3">attribute_set</li>
</ul>
<ul id="eleGroups">
<li class="sortme group attribute required" id="group-weight"></li>
<li class="sortme group attribute required" id="group-visibility"></li>
<li class="sortme group attribute" id="group-status"></li>
<li class="sortme group attribute" id="group-short_description"></li>
</ul>
<ul class='custom-menu' id="elementMenu">
<li class='visibleElement' data-action="duplicate">Duplicate</li>
<li class='visibleElement' data-action="delete">Delete</li>
<li class='visibleElement' data-action="copy">Copy</li>
<li class='visibleElement' data-action="cut">Cut</li>
</ul>
<ul class='custom-menu' id='attributeMenu'>
<li class='visibleAttribute' data-action="paste">Paste</li>
</ul>
</div>
CSS:
ul {
padding: 0;
}
#elePool,
#eleGroups {
float:left;
margin-right:30px;
width:300px;
border:1px solid #808080;
min-height:25px;
}
#eleGroups {
min-height:300px;
}
li.selected {
background-color: #DAA520 !important;
}
#eleGroups .group li,
#elePool li {
border:1px solid #808080;
background-color:#E0FFFF;
line-height:25px;
cursor: -webkit-grab;
cursor: move;
text-indent:15px;
list-style: none;
}
#eleGroups > li {
position:relative;
box-sizing: border-box;
min-height:100px;
border:1px dashed #D3D3D3;
padding-top: 20px;
list-style: none;
}
#eleGroups > li:after {
position:absolute;
top:1px;
left:2px;
font-size:16px;
text-transform: uppercase;
color: #808080;
}
li.group {
float: left;
width: 33.3%;
}
ul#eleGroups {
width:50%;
}
.required {
background-color:#FFEBE8;
}
.complete {
background-color:#EEFFAA !important;
}
/* The whole thing */
.custom-menu {
display: none;
z-index: 1000;
position: absolute;
overflow: hidden;
border: 1px solid #CCC;
white-space: nowrap;
font-family: sans-serif;
background: #FFF;
color: #333;
border-radius: 5px;
padding: 0;
}
/* Each of the items in the list */
.custom-menu li {
padding: 8px 12px;
cursor: pointer;
list-style-type: none;
}
.custom-menu li:hover {
background-color: #DEF;
}
#group-weight:after {content: 'weight'}
#group-visibility:after {content: 'visibility'}
#group-status:after {content: 'status'}
#group-short_description:after {content: 'short_description'}
JS:
function addElementMenu() {
$('.element').on("contextmenu", function (e) {
// Avoid the real one
e.preventDefault();
e.stopPropagation();
//set clicked item
clicked = $(this);
parent = $(this).parent().attr('id');
// Show contextmenu
$("#elementMenu").finish().show().css({
top: event.pageY + "px",
left: event.pageX + "px",
display: 'block'
});
});
}
function addAttributeMenu() {
$('.attribute').on("contextmenu", function (e) {
// Avoid the real one
e.preventDefault();
e.stopPropagation();
//set clicked item
clicked = $(this);
parent = $(this).parent().attr('id');
// Show contextmenu
$("#attributeMenu").finish().show().css({
top: event.pageY + "px",
left: event.pageX + "px",
display: 'block'
});
});
}
$('.sortme').on('click', 'li', function(e) {
if(e.ctrlKey || e.metaKey) {
$(this).toggleClass("selected");
} else {
$(this).addClass("selected").siblings().removeClass('selected');
}
}).sortable({
connectWith: ".sortme",
delay: 150,
revert: 0,
helper: function (e, item) {
var helper = $('<li/>');
if(!item.hasClass('selected')) {
item.addClass('selected').siblings().removeClass('selected');
}
var elements = item.parent().children('.selected').clone();
item.data('multidrag', elements).siblings('.selected').remove();
return helper.append(elements);
},
stop: function(e, info) {
info.item.after(info.item.data('multidrag')).remove();
},
update: function(event, ui) {
checkRequired(this);
}
});
function checkRequired(elementToCheck) {
if($(elementToCheck).hasClass('required')) {
if($(elementToCheck).is(':empty')) {
$(elementToCheck).removeClass('complete');
} else {
$(elementToCheck).addClass('complete');
}
}
}
//add the context menus
addElementMenu();
addAttributeMenu();
// If the document is clicked somewhere
$(document).bind("mousedown", function (e) {
// If the clicked element is not the menu
if (!$(e.target).parents(".custom-menu").length > 0) {
// Hide it
$(".custom-menu").hide(100);
}
});
// If the menu element is clicked
$(".custom-menu li").click(function(){
// This is the triggered action name
switch($(this).attr("data-action")) {
// A case for each action. Your actions here
case "duplicate": duplicateItem(clicked); break;
case "delete": deleteItem(clicked); break;
case "copy": copyItem(clicked); break;
case "cut": cutItem(clicked); break;
case "paste": pasteItem(); break;
}
// Hide it AFTER the action was triggered
$(".custom-menu").hide(100);
});
When you load js file?
If you load js files above the dom elements you handle,
your javascript code $( selector ) will not work properly.
Maybe Codepen and fiddle load your js files after dom elements, so the error will not occurs.
check your html file's javascript load point.
Many people likes following.
<html>
<head>
</head>
<body>
<!-- your dom elements -->
<script src="yourJavascript.js"></script>
</body>
</html>
If you load js files just before </body>, you can use your dom elements in yourJavascript.js
I had the same error, although in a somewhat different scenario (I was adding items manually to a sortable list). My problem was also highly sensitive to timing and environment options, sometimes it ran ok, sometimes not.
This stack overflow answer helped me onto the right track (I needed to clone the object before I added it to the list)
Please try to adding containment in sortable.
Default: false
Defines a bounding box that the sortable items are constrained to while dragging.
Note: The element specified for containment must have a calculated width and height (though it need not be explicit). For example, if you have float: left sortable children and specify containment: "parent" be sure to have float: left on the sortable/parent container as well or it will have height: 0, causing undefined behavior.
$( ".selector" ).sortable({
containment: "parent"
});
Have you tried wrapping the whole thing in jQuery, which would delay execution until DOMReady.
$(function () { /*...YOUR CODE HERE...*/ });
Related
Can someone explain me what I'm doing wrong, if you click on the first link(link 1) it opens a menu, if you click on one of the 'li' inside the menu it closes the menu.
If I click on the second link(link 2) it opens a different menu but when I click on one of the 'li' inside the menu nothing happens, and what I am trying to do is to close the menu.
jsfiddle (http://jsfiddle.net/BdhxL/)
The HTML code:
Link 1
<div id="dropMenu">
<ul>
<li>Portfolio</li>
</ul>
</div>
<br><br>
Link 2
<div id="dropMenu">
<ul>
<li>Contact us </li>
</ul>
</div>
The JS code:
$(document).ready(function() {
$("li").click(function()
{
$("#dropMenu").hide("slow");
});
$("a").click(function()
{
$(this).toggleClass("active");
$(this).next("div").stop('true','true').slideToggle("slow");
});
});
#dropMenu {
display: none;
position: relative;
right: 5px;
background: #000;
color: black;
margin:50px -5% 0% -142%;
padding: 0px 0px 0px 10px;
}
#dropMenu a {
display: block;
color: #fff;
text-decoration: none;
padding:10px 6px;
font-weight:400;
border-bottom: solid 1px #fff;
}
The CSS code:
#dropMenu ul {
margin:0;
}
#dropMenu a:hover {
background: #57585A;
}
#dropMenu ul {
list-style:none;
padding:0;
}
You have duplicate ids.$("#dropMenu").hide("slow"); is always targetting first dropMenu. Use $(this) to target current and hide the closest div.Try this:
$(document).ready(function() {
$("li").click(function()
{
$(this).closest("div").hide("slow");
});
$("a").click(function()
{
$(this).toggleClass("active");
$(this).next("div").stop('true','true').slideToggle("slow");
});});
Working Demo
Currently, you're having duplicated id for parent div of your list which is <div id="dropMenu"> , you need to use class instead:
<div class="dropMenu">
then you can use .closest() to target closest matching .dropMenu of clicked li:
$(document).ready(function () {
$("li").click(function () {
$(this).closest(".dropMenu").hide("slow");
});
$("a").click(function () {
$(this).toggleClass("active");
$(this).next("div").stop('true', 'true').slideToggle("slow");
});
});
You also need to change all #dropMenu to .dropMenu in your CSS.
Updated Fiddle
I have a context menu in jquery on Right Click.
But it is somehow not fulfilling my needs.
When i add new div on click and then try to have context menu operation on it, then it is not working.
It is applying operation on original div.
Can someone help me out in getting this problem solved and improving my Jquery or HTMl.
Js fiddle for Context Menu
Thanks
As marck said that there are many mistakes in your code.You used same ID on multiple elements multiple times. Anyway, I created a basic jsfiddle of what you are trying to achieve. You can build on top of that and modify it according to your needs.
Here is the link:
http://jsfiddle.net/PCLwU/
function add(){
//For adding new items.
}
function menu(){
//to show up context menu
}
function menuactions(){
//Define the actions performed when menu option is selected.
}
For different context menu for different list : http://jsfiddle.net/PCLwU/3/
Context menu div
<div id='contextMenu'>
<ul id='items'>
<li id="cutDoc">Cut</li>
<li id="copyDoc">Copy</li>
<li id="pasteDoc">Paste</li>
<li id="deleteDocs">Delete</li>
</ul>
</div>
menu Style
<style>
#items
{
list-style: none;
margin: 5px 0 0 5px;
}
#contextMenu
{
display: none;
position: fixed;
border: 1px solid grey;
width: 150px;
background-color:white;
box-shadow: 2px 2px 1px grey;
}
#items li
{
list-style-type: none;
border-bottom: 1px solid grey;
border-bottom-style: dotted;
padding: 10px;
font-size: 14px;
}
#items :hover
{
background: #0070FF;
color: white;
}
</style>
jQuery Script for applying on area where it will needed which
$("YOur class name").mousedown(function(e){
//to block browsers default right click
if( e.button == 2 ) {
$("#contextMenu").css("left", e.pageX);
$("#contextMenu").css("top", e.pageY);
$("#contextMenu").fadeIn(500, startFocusOut());
}
});
function startFocusOut() {
$(document).on("click", function () {
$("#contextMenu").hide(500);
$(document).off("click");
});
}
This will work fine.
Update:
here is the fiddle demo
I am trying to remove the selected item who class has "selected" but instead of just deleting the LI item, the entire list is being cleared out. I am using jQuery for this.
I've put together a quick fiddle:
http://jsfiddle.net/6QvvC/4/
<!DOCTYPE html>
<html>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<script src="jquery.min.js"></script>
<head>
<style type="text/css">
* {
font-size: 9pt;
font-family: Segoe UI;
}
#refdocs {
border: 0;
padding: 2px;
}
#box1 {
border: 1px solid rgb(170,170,170);
width: 200px;
}
#box2 {
width: 100%;
display: block;
position: relative;
border-bottom: 1px solid rgb(170,170,170);
}
#container {
height: 100px;
overflow-y: scroll;
overflow-x: hidden;
}
#list1 {
width: 100%;
}
#list1 ul {
margin: 0;
padding: 0px;
list-style-type: none;
}
#list1 li {
cursor: default;
padding: 2px;
}
.selected {
background: rgb(228,228,228);
}
</style>
<script type="text/javascript">
window.onload = function() {
refresh_list()
}
function remove_selected_item() {
if ( $('#list1 ul li').hasClass("selected") ) {
alert("yup")
$('#list1 ul li').remove()
}
else {
alert("nope")
}
}
function refresh_list() {
$('#list1 ul li').click(function () {
$('#list1 ul li').removeClass('selected');
$(this).addClass('selected');
document.getElementById('refdocs').value = $(this).text()
});
}
</script>
</head>
<body>
<div id="box1">
<div id="box2"><input type="text" id="refdocs"></div>
<div id="container">
<div id="list1">
<ul>
<li>Coffee</li>
<li>Tea</li>
<li>Milk</li>
</ul>
</div>
</div>
</div>
<input type="button" value="delete" onclick="remove_selected_item()">
</body>
</html>
Function can be simplified:
function remove_selected_item() {
$('#list1 ul li.selected').remove()
}
You need to removed selected item - so you select the li with class .selected and just remove it.
Demo: http://jsfiddle.net/6QvvC/3/
The jQuery selector #list1 ul li matches ALL li elements inside an ul inside an element with id list1.
hasClass returns true if ANY of the matched elements contains the given class.
remove removes all matched elements, which in the given case is all list elements. That is why the list is cleared.
Maybe dive into the power of jQuery selectors a bit: http://codylindley.com/jqueryselectors/
You can not only select elements based on their type or ID, but also on their class, their attributes, their position in the DOM (parents, siblings, children) and their state (e.g. hover).
When installing click handlers on list elements, the event delegation pattern is also pretty useful: https://learn.jquery.com/events/event-delegation/ It might help you to get a better understanding of how events and handler installation work with jQuery. It at least was some kind of revelation for me.
I don't have much experience with jQuery.
I have 4 li elements and a draggable block. When I drag from green to blue, first I need my dragging block to stand behind blue and not go out from the borders of the container, meaning that my draggable element must be always snap into the container.
When my draggable element stand behind blue, the background color must be changed from brown to red.
Can anybody help me? Because I don't really know how to do this. All what I have at this moment is: JSFiddle
HTML
<div id="container" class="ui-widget-header">
<div id="draggable" class="ui-widget-content"></div>
<ul id="menu">
<li id="menuElement1"></li>
<li id="menuElement2"></li>
<li id="menuElement3"></li>
<li id="menuElement4"></li>
</ul>
</div>
CSS
body {
background-color: brown;
}
#container {
position: relative;
}
#menu,
#draggable {
position: absolute;
top: 0;
left: 0;
}
#draggable {
width: 100px;
height: 150px;
}
#menu {
margin-left: -40px;
}
ul li {
display: block;
float: left;
padding: 50px;
}
#menuElement1 {
background-color: green;
}
#menuElement2 {
background-color: blue;
}
#menuElement3 {
background-color: black;
}
#menuElement4 {
background-color: red;
}
#container {
height: 150px;
width: 100%;
background-color: gray;
JQuery
$(function() {
$( "#draggable" ).draggable({ snap: ".ui-widget-header" });
});
Here's an updated fiddle
You'd do well to read this article on Droppable from the jQuery docs. Basically you were half way there, you need to make an element (the blue box, with id #menuElement2) droppable to provide a target for a draggable element
$('#menuElement2').droppable({
drop: function( event, ui ) {
//Do something here...
}
});
Within the drop function (this fired when the block is dropped on the target) you need to perform your action.
$('body').css({'background-color': 'red'});
This adds a custom css rule to the body that changes the colour, again check out the jQuery docs - search for 'jQuery css'.
Hope this helps :)
Update: I just updated the fiddle again to make it revert back to brown when you change selection - not sure if you wanted this but it can be easily removed by commenting out the css change within the drag function.
I'm rather new to jQuery and I'm trying to make a cool little menu effect where when the user hovers over a element (#nav li) it will animate to a larger width, which will reveal the full background image. Each of the menu items has a ID to explicitly set a width (since they are all different in size), so #nav1 might be 80px whereas #nav2 is 90px. So I found this: How to get all of the IDs with jQuery? and that helped me to create a array but now I'm having problem figuring out how to insert it into the animation. I figured I would need to do a each or for loop. But like I said I'm rather new to jQuery and am having some problems.
So basically I'd like the variable chgWidth to return the width() of the currently hovered #nav and then I would plug that variable into the animate except I would add 30px for instance, or on the hover off I would subtract 30px.
Any idea? Here is my current code...
$(function(){
var chgWidth = $("#nav [id]").map(function(id) {
return this.id;
});
$.each(chgWidth,function(n,value){
$('#nav li').hover(function() {
$(this).stop(0,1).animate({"width" : chgWidth+"px" });
},
function(){
$(this).stop(0,1).animate({ "width" : chgWidth+"px" });
});
});
Sample HTML
<div id="menu">
<ul id="nav">
<li id="nav1"><a alt="" href="#">home</a></li>
<li id="nav2"><a alt="" href="#">about us</a></li>
<li id="nav3"><a alt="" href="#">weddings & events</a></li>
<li id="nav4"><a alt="" href="#">gallery</a></li>
<li id="nav5"><a alt="" href="#">accolades</a></li>
<li id="nav6"><a alt="" href="#">blog</a></li>
<li id="nav7"><a alt="" href="#">contact us</a></li>
</ul>
</div>
Sample CSS:
#menu { width: 100%; overflow: hidden; padding:0px 0px; background: #ffc4a0;}
#nav { position: relative; left: 50%; float: left;}
#nav li { position: relative; right: 50%; float: left; padding: 0 5px; margin: 0 5px; overflow:hidden; }
#nav1 { width:55px; }
#nav2 { width:80px; }
#nav3 { width:175px; }
#nav4 { width:60px; }
#nav5 { width:85px; }
#nav6 { width:40px; }
#nav7 { width:100px; }
#nav li a { color: #ffffff; text-decoration: none; font: bold 16px Verdana, Arial, Helvetica, sans-serif; }
Based on the answer I got this is what I ended up doing and it seems to work good (Thanks to Joel and krdluzni):
$(function(){
$("#nav [id]").each(function(){
$(this).hover(function() {
$(this).stop(0,1).animate({"width" : "+=30" });
},
function(){
$(this).stop(0,1).animate({ "width" : "-=30" });
});
});
});
jQuery as of a short while ago accepts a += like syntax for widths and other numeric values in animate. See the second last paragraph in the overview (and the examples as well) here: http://docs.jquery.com/Effects/animate#paramsdurationeasingcallback
You don't actually need to get the ids in an array so much as you need to assign an animation to each element in nav that has an id.
Instead of mapping the ids to your chgWidth variable, use each to iterate over the collection of elements and set each one individually.
$(function(){
$("#nav [id]").each(function(){
$(this).hover(function() {
$(this).stop(0,1).animate({"width" : this.id+"px" });
},
function(){
$(this).stop(0,1).animate({ "width" : this.id+"px" });
});
});
});
When you use each to iterate over a collection of elements, the this context is set to the current iterating element.
No each function is required! If a selector returns multiple elements, all of the elements recieve the function chained to them.
This is almost right:
$("li #nav").hover(
function()
{
$(this).stop(0,1).animate({"width" : this.width()+30 });
//save original width somehow
},
function()
{
$(this).stop(0,1).animate({ "width" : this.width()-30 });
//retrieve original width
});
);
You just need to save the widths somehow. Perhaps they can be read from the CSS.