This little script makes list items selectable and draggable. I'm pretty new to programming in general, and even more so to Javascript. Most of my background is in PHP and some C++. On the large, I don't think this is the proper way to be writing code in JavaScript. I know global variables are bad, but I'm not used to dealing with event handling, I don't know how to maintain constants like that throughout various event calls that aren't otherwise attached to each other. I'm really not sure if I'm doing any of this right.
A more specific question - This script works as is, but if i change handleDragStart()
's variable dragSrc = this;
into var dragSrc = this;
as it should be, it breaks. When the handleDrop()
event fires, it returns dragSrc is not defined
. If anything, I would expect this behavior with the way it is written now, not after adding var
. I'm a little lost.
var SELECTED_CLASS_CONST = 'selected';
var DROP_CLASS_CONST = 'drophover';
function handleDragStart(e) {
dragSrc = this;
e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.setData('text/html', this.innerHTML);
}
function handleDragEnter(e) {
this.addClass(DROP_CLASS_CONST);
}
function handleDragLeave(e) {
this.removeClass(DROP_CLASS_CONST);
}
function handleDragEnd(e) {
[].forEach.call(draggables, function(drag) {
drag.removeClass(DROP_CLASS_CONST);
});
}
function handleDragOver(e) {
if (e.preventDefault) {
e.preventDefault();
}
e.dataTransfer.dropEffet = 'move';
return false;
}
function handleDrop(e) {
if (e.stopPropagation)
{
e.stopPropagation();
}
if ( dragSrc != this )
{
dragSrc.innerHTML = this.innerHTML;
this.innerHTML = e.dataTransfer.getData('text/html');
this.removeClass(DROP_CLASS_CONST);
if (dragSrc.hasClass(SELECTED_CLASS_CONST) && this.hasClass(SELECTED_CLASS_CONST))
{ return; }
else { swapClass(dragSrc, this, SELECTED_CLASS_CONST); }
}
return false;
}
function toggleClass(e) {
if (e.preventDefault) {
e.preventDefault();
}
this.toggleClass(SELECTED_CLASS_CONST);
}
function swapClass(elemA, elemB, c) {
if (elemA.hasClass(c) || elemB.hasClass(c)) {
elemA.toggleClass(c);
elemB.toggleClass(c);
}
return;
}
Element.prototype.toggleClass = function(name) {
if (this.hasClass(name))
{
this.removeClass(name);
}
else
{
this.addClass(name);
}
};
Element.prototype.hasClass = function(name) {
return new RegExp("(?:^|\\s+)" + name + "(?:\\s+|$)").test(this.className);
};
Element.prototype.addClass = function(name) {
if(!this.hasClass(name))
{
this.className = this.className ? [this.className, name].join(' '): name
}
};
Element.prototype.removeClass = function(name) {
if(this.hasClass(name))
{
var curClass = this.className;
this.className = curClass.replace(new RegExp("(?:^|\\s+)" + name + "(?:\\s+|$)", "g"), "");
}
};
var draggables = document.getElementsByClassName('drag');
[].forEach.call(draggables, function(drag) {
drag.addEventListener('dragstart', handleDragStart, false);
drag.addEventListener('dragenter', handleDragEnter, false);
drag.addEventListener('dragover', handleDragOver, false);
drag.addEventListener('dragleave', handleDragLeave, false);
drag.addEventListener('dragend', handleDragEnd, false);
drag.addEventListener('drop', handleDrop, false);
drag.addEventListener('click', toggleClass, true);
});
I know I could just use jQuery or the myriad of other js libraries, but this is just a project to really learn Javascript thoroughly before moving to a library. At least how to properly structure my code (something I'm still learning how to do in PHP as well...) form closures when necessary, and how to recognize when that is.
Edit: JSFiddle (warning: I don't think preventDefault()
works in the jsfiddle window, so it will probably redirect you, I didn't test with other browsers.) and another example with source. The second example has a similar code structure, but it's obviously a quick and dirty example; I want to know if there is a better way to do it. It mostly being event binding and maintaining a "const
" between various unassociated event function calls.
var
: a variable declared with var is in the local scope, one without is in the global scope. So when you assign to it in one function, it is available in the other as the variable is in the global scope; while when you usevar dragSrc = this;
then dragSrc is only available inside that function (... so while outside, you will access the global-scope variable with the same name, which is undefined). \$\endgroup\$