Okay, to first address a couple things:
1.This is not the ideal approach to creating objects, which is what you are looking to do. In the example, I have modified it a bit, but I would still rewrite it using the prototype approach. What you have here will create these functions for each instance of the object, loading way more than you need to into memory.
2.On the textareas with the onKeyPress, you cannot reference the tagOnKeyPress function that is part of the tagEditor object. It's not defined in the public scope.
3.When adding eventListeners, don't execute the function in the definition for the handler unless you are returning a function as a handler.
tagInput.addEventListener('keyup', tagOnKeyPress(event.which));
tries to execute "tagOnKeyPress" immediatley. Instead, pass just the function reference, and expect an "event" type passed to it. Then, define the keyCode in the function.
tagInput.addEventListener('keyup', this.tagOnKeyPress); //13-ERROR this.tagOnKeyPress = function(event) { var keyCode = event.which; }
4.As someone else pointed out, when you are referencing the functions defined within the scope of a function (your object), you need to use "this" as a prefix.
I changed the code a bit to define a "TagEditor" object for use with your loop. You could add the functions to the TagEditor prototype to increase performance and extensibility.
Code:
var TagEditor = function () { var edit, editTag, tagList, tagInput, tagOutput, i; this.init = function () { edit = false; tagList = document.querySelectorAll('[class=tagList]').item(i); //L.96/97 #1 tagInput = tagList.querySelectorAll('[name=tagInput]')[0]; tagOutput = tagList.querySelectorAll('[name=tags]'); tagList.addEventListener('mouseout', function () { if (tagList.ownerDocument.activeElement.parentNode !== tagList && !isHover(tagList)) { tagList.style.maxHeight = '40px'; } }); tagInput.addEventListener('keyup', this.tagOnKeyPress); //13-ERROR tagInput.addEventListener('focus', this.changeSize); tagInput.addEventListener('blur', this.changeSize); for (var i = 2; i < tagList.childNodes.length; i++) { tagList.childNodes[i].addEventListener('click', this.tagOnClick); tagList.childNodes[i].addEventListener('mousemove', this.tagOnMouseMove); tagList.childNodes[i].addEventListener('mouseout', this.tagOnMouseOut); } }; this.tagOnKeyPress = function (e) { var keyCode = e.which; var tagInputValue = tagInput.value.trim(); if (tagInputValue.length !== 0) { if (edit) { if (keyCode === 13) { edit = false; editTag.style.borderColor = '#ddd'; tagInput.value = ''; return; } tagOutput.value = tagOutput.value.replace(editTag.innerHTML, tagInputValue); newTag = editTag; } else { if (keyCode !== 13) return; tagOutput.value = tagOutput.value + tagInputValue + ';'; var newTag = document.createElement('div'); newTag.addEventListener('click', tagOnClick); newTag.addEventListener('mousemove', tagOnMouseMove); newTag.addEventListener('mouseout', tagOnMouseOut); } newTag.innerHTML = tagInputValue; tagList.appendChild(newTag); } if (!edit) tagInput.value = ''; }; this.tagOnClick = function () { if ((this.offsetWidth + getOffsetLeft(this) - event.pageX) < parseInt(this.style.backgroundSize, 10)) { tagOutput.value = tagOutput.value.replace(this.innerHTML + ';', ''); this.parentNode.removeChild(this); tagInput.value = ''; edit = false; } else { setEdit(this); } tagInput.focus(); }; this.tagOnMouseMove = function () { if ((this.offsetWidth + getOffsetLeft(this) - event.pageX) < parseInt(this.style.backgroundSize, 10)) { this.style.backgroundSize = '16px'; } else { this.style.backgroundSize = '18px'; } }; this.tagOnMouseOut = function () { this.style.backgroundSize = '18px'; }; this.setEdit = function (tag) { edit = true; editTag = tag; tag.style.borderColor = '#297CCF'; tagInput.value = tag.innerHTML; }; this.changeSize = function (e) { if (e.type === 'focus') { tagList.style.maxHeight = 'none'; } else if (e.type === 'blur' && !isHover(tagList)) { tagList.style.maxHeight = '40px'; } }; this.isHover = function (elem) { return (elem.parentElement.querySelector(':hover') === elem); }; this.getOffsetLeft = function (elem) { var offsetLeft = 0; while (true) { //521px over while scrolling to the right ---> (-TABLE:521px) if (elem.nodeName !== 'TABLE') offsetLeft += elem.offsetLeft; elem = elem.parentNode; if (elem.nodeName === 'HTML') break; } return offsetLeft; }; }; function tagEditorsInit() { var tagEditors = []; var tagLists = document.getElementsByClassName('tagList'); for (var i = 0; i < tagLists.length; i++) { var tagEditor = new TagEditor(); tagEditor.tagList = tagLists.item(i); // Why isn't it working??? #1 tagEditor.i = i; tagEditor.init(); tagEditors.push(tagEditor); } } window.addEventListener('load', tagEditorsInit);
Now, I will admit that I don't use the Prototype approach everywhere. Sometimes, I write a small object that I need to use in a specific instance and I don't approach it the same way I would with a complex object. But, when writing jQuery plug-ins, or special scripts for custom applications, the Prototype approach is much easier to read, breakdown, and work with.
What does this really mean? The prototype approach defines the functions with the object type, as opposed to being defined each time an instance of the object is created. To give an example using the code above, each time you create a TagEditor, that tagEditor object defines all of those functions all over again for that specific tagEditor object. If you were able to expand or open up the individual objects, it would be like reading all of those functions defined for every single one.
With the prototype approach, it's more or less class based, like an object oriented lang like C++ or C# would have. Whenever you call the "tagOnKeyPress" function, it uses the one associated with the TagEditor type. Not the one defined with the "tagEditor" object that is of the TagEditor type. In short, the function is only defined once, and inherits access to the properties defined in the "tagEditor" object of that type.
And in case I didn't confuse you enough with that explanation, check out this MDN article which does a good job showing examples of all of this and how it differs:
Update This update is in response to your comment and Fiddle. And I just worked through your Fiddle with some modifications. Hopefully you can see what I changed. Provided below are some additional comments
1.The definition for this.tagInput needs to reference a node, not the NodeList. There are a couple spots I think you are expecting a single node but are getting a NodeList of length 1. To reference the actual DOM element to assign an EventListener, you just need to specify the 0 index of the NodeList.
2.When you bind a function as a handler, you don't want to execute the function unless you are returning a function. Bullet 3 above shows an example of this. When you add parenthesis after a function name (myMouseHandler()) it executes the function at that moment. It doesn't wait until the even occurs.
3.When you are working within a specific scope, you can define local variables to simplify working with object properties. For instance, if you use "this.tagList" much, you might tired of typing "this.tagList" everywhere. When you define it, you can assign the property and a local var like so:
var tl = this.tagList = document.getElementsByClassName('tagList').item(tagListID);
and you can then use "tl" within that scope.
this.tagOuput.value = tl.value;
4.When assigning event handlers using Prototype, you still need a way to reference the main Object you're working with. An anonymous function at the eventListener binding provides an easy way to do this where needed.
// function is the actual handler. e = event, this = element, te = tagEditor tagInput.addEventListener('mouseout', function(e) { te.tagOnClick(e, this); });
I tried to clean up your fiddle a bit and get working what I could guess you were attempting. There is probably more you need but this might provide a clearer position for you to work from.
Fiddle: https://jsfiddle.net/938z8x98/13/
new function(){…}to instantiate objects!