Attached event listener on mouseover - javascript

I'm very new to Javascript and is trying to attach an anonymous function to an event listener. What the function does is that when mouseover the first element, a message will be displayed in the second element depending on the length of the text within the first element.
However, when I hover my mouse over the first element, nothing happens. Because I'm new to JavaScript, I'm not sure what I did wrong.
function checkLength(event, minLength){
var el, elementTwo;
el = event.target;
elementTwo = el.nextSibling;
if(el.value.length < minLength){
elementTwo.innerHTML = "not enough";
} else {
elementTwo.innerHTML = "enough";
}
}
var elUserName = document.getElementById("one");
elUserName.addEventListener("mouseover", function(event){
checkLength(event, 5);
}, false);
<div>
<div id="one">Bob</div>
<div id="two"></div>
</div>

To access the text of the element you use textContent. value is for inputs.
Also, you need to select the next element sibling, not just the next sibling node.
function checkLength(event, minLength){
var el, elementTwo;
el = event.target;
elementTwo = el.nextElementSibling;
if(el.textContent.length < minLength){
elementTwo.innerHTML = "not enough";
} else {
elementTwo.innerHTML = "enough";
}
}
var elUserName = document.getElementById("one");
elUserName.addEventListener("mouseover", function(event){
checkLength(event, 5);
}, false);
<div>
<div id="one">Bob</div>
<div id="two"></div>
</div>

Related

Javascript event handler won't change another element's event handler

I have two divs, div1 and div2. When div2 is clicked, I want to change the "onmouseover" handler for div1. Here is fiddle code (https://jsfiddle.net/au43obnz/2/):
<div id='div1'
onmouseover='this.style.color=`purple`'
onmouseout='this.style.color=`black`'
onclick='this.onmouseover=null; this.onmouseout = null;'>
hello
</div>
<br>
<div id='div2'
onclick="div1 = document.getElementById(`div1`); div1.style.color=`blue`; div1.onmouseover = 'this.style.color=`yellow`';">
world
</div>
div2's onclick handler is working when I try to change another element of div1 (e.g. div1.style.color='blue'), and div1's onclick handler is successfully changing the onmouseover function for itself (e.g. onclick='this.onmouseover=null; this.onmouseout = null;).
But the div2 onclick handler won't change the onmouseover function for div1. I've tried changing div1.onmouseover = "this.style.color='yellow'" to div1.onmouseover = "document.getElementById('div1').style.color='yellow'", but it still doesn't work.
Anyone know what's going on here?
A control variable can be used to drive the program. In this way, the necessary conditions for directing the program are checked. In the solution below, the old mouseover event of the first <div> element is fired unless the second <div> is clicked; After clicking the second <div>, the new mouseover event of the first <div> is fired.
const div1 = document.getElementById('div1');
const div2 = document.getElementById('div2');
let passive = false, newEventHandler = false;
/* old mouse over event */
div1.addEventListener('mouseover', function() {
if(!passive && !newEventHandler) {
this.style.color = "purple";
console.log("old mouse over");
}
});
/* new mouse over event */
div1.addEventListener('mouseover', function() {
if(newEventHandler) {
this.style.color = "yellow";
console.log("new mouse over");
}
});
/* passive mouse out event */
div1.addEventListener('mouseout', function() {
if(!passive)
this.style.color = "black";
});
/* conditions */
div1.addEventListener('click', function() {
passive = true;
});
/* conditions */
div2.addEventListener('click', function() {
div1.style.color = "blue";
newEventHandler = true;
});
<div id='div1'>hello</div><br>
<div id='div2'>world</div>

How do I make "Enter" keypress in an <input> Element shift focus to the next <input> element on the page. (JavaScript)

How do I make make an Enter keypress in an <input> element shift focus to the next <input> element on the page?
I have a for loop that creates <li> elements with <input> elements inside. I need to make so that when the user hits enter on their keyboard, the website will focus on the next input field so that the user can enter the next player name without having to toggle between using their mouse and their keyboard.
I thought using the nextSibling property was the solution but it wont work because the <input> elements technically dont have any siblings because each of them is inside/are children of diferent <li> elements.
Here is my JavaScript:
for ( var i = 1 ; i <= numberOfPlayers ; i++ ){
var inputElement = document.createElement('input');
var liElement = document.createElement('li');
inputElement.setAttribute( 'type' , 'text' );
inputElement.setAttribute ( 'id' , 'name-input-' + i );
inputElement.setAttribute ( 'class' , 'name-input');
inputElement.setAttribute ( 'placeholder' , 'Enter a name for player ' + i );
liElement.appendChild(inputElement);
nameInputArray[i] = inputElement;
document.getElementById('name-list').appendChild(liElement);
inputElement.addEventListener( 'keypress' , function(event){
if ( event.which === 13 ) {
alert(this);
document.getElementById( 'name-input-' + (i+1)).focus();
}
} );
}
I tried using the "i" in the for loop and string concatenation to select the ID of the next element but the "i" variable isn't working either because by the time that code runs that "i" is equal to the highest number that it can be after the whole for loop has ran.
Problem:
The problem with your actual code is that i is always equal to numberOfPlayers+1 in the event handler callback function, so you were trying to focus on a null element, you can read more about JavaScript closures to see why i was always equal to numberOfPlayers+1.
Solution:
First you need to use the onkeypress event on your input, then test if the pressed key is the Enter, if it's pressed get the id of the current input, extract i value from it and focus on the next input element using the next id.
This is how should be your code:
inputElement.addEventListener('keypress', function(event) {
if (event.which === 13) {
var i = parseInt(this.id.charAt(this.id.length-1));
console.log(i);
if(i<=numberOfPlayers){
document.getElementById('name-input-' + (i + 1)).focus();
}
}
});
This is a working snippet:
var numberOfPlayers = 5;
var nameInputArray = [];
for (var i = 1; i <= numberOfPlayers; i++) {
var inputElement = document.createElement('input');
var liElement = document.createElement('li');
inputElement.setAttribute('type', 'text');
inputElement.setAttribute('id', 'name-input-' + i);
inputElement.setAttribute('class', 'name-input');
inputElement.setAttribute('placeholder', 'Enter a name for player ' + i);
liElement.appendChild(inputElement);
nameInputArray[i] = inputElement;
document.getElementById('name-list').appendChild(liElement);
inputElement.addEventListener('keypress', function(event) {
if (event.which === 13) {
var i = parseInt(this.id.charAt(this.id.length - 1));
console.log(i);
if(i<numberOfPlayers){
document.getElementById('name-input-' + (i + 1)).focus();
}
}
});
}
<ul id="name-list"></ul>
If you want to stick with vanilla JS, use this:
for (var i = 1; i <= numberOfPlayers; i++) {
var inputElement = document.createElement("input");
var liElement = document.createElement("li");
inputElement.type = "text";
inputElement.id = "name-input-" + i;
inputElement.className = "name-input";
inputElement.placeholder = "Enter a name for player " + i;
liElement.appendChild(inputElement);
nameInputArray[i] = inputElement;
document.getElementById("name-list").appendChild(liElement);
(function(i) {
inputElement.addEventListener("keypress", function(event) {
if (event.which === 13) {
alert(this);
document.getElementById("name-input-" + (i + 1)).focus();
}
});
})(i);
}
This is my solution.
Do not forget that the created <li> element needs to be appended to something like <body>. I have actually added a <ul> element and appended it to the <body> and then appended the <li> elements to the <ul> element.
If you use nextSibling, you do not need to keep elements in nameInputArray. I have not removed it to show how it should be initialized before you can use it in your loop. Also, nextSibling works in my solution since I have put all the <li>s under one <ul> which I think is the correct thing to do anyway.
Other than that, I just corrected a few things here and there. Let me know if you have more questions about this code.
function eventFunc(event) {
if (event.which === 13) {
var nextInput = event.target.parentElement.nextSibling.childNodes[0];
if (nextInput !== null)
nextInput.focus();
}
}
var numberOfPlayers = 4;
var nameInputArray = [];
var ulElement = document.createElement('ul');
document.body.append(ulElement);
for (var i = 0; i < numberOfPlayers; i++) {
var liElement = document.createElement('li');
ulElement.append(liElement);
var inputElement = document.createElement('input');
inputElement.setAttribute('type', 'text');
inputElement.setAttribute('id', 'name-input-' + i);
inputElement.setAttribute('class', 'name-input');
inputElement.setAttribute('placeholder', 'Enter a name for player ' + i);
inputElement.setAttribute('onkeypress', "eventFunc(event)");
liElement.appendChild(inputElement);
nameInputArray[i] = inputElement;
}
UPDATE: Getting each input box from parent <li> elements:
After comment from the OP, I see that they want a structure like this:
<ul>
<li>input box1</li>
<li>input box2</li>
<li>input box3</li>
</ul>
In this structure, each input box is the first child node of its parent <li> element. Therefore, we can still use nextSibling (as the OP intended to use) in this way:
nextInput = event.target.parentElement.nextSibling.childNodes[0];
This line first finds the parent <li> element, applies nextSibling to get the next li element and then gets the input box inside that element.
$('input').on('keyup', function(event){
if(event.keyCode == 13){ // 13 is the keycode for enter button
$(this).next('input').focus();
}
});
https://jsfiddle.net/jat1merL/
are you looking for this?
by the way, use keyup instead of keypress. if a key is hold it fires mass of keypress events in a speed you can't handle ;)

applying an event handler to newly created objects

So my goal is to have 5 boxes and every time one box is clicked a new box appears. The code I wrote for that is this:
window.onload = function(){
var boxList = document.getElementsByClassName("box");
for(i = 0; i< boxList.length;i++){
boxList[i].onclick = clickHandler;
}
}
function clickHandler(eo){
if(eo.target.style.backgroundColor != "black") {
eo.target.style.backgroundColor = "black";
var box = document.createElement("div");
box.setAttribute("class","box");
box.setAttribute("id",document.getElementsByClassName("box").length++);
document.getElementById("Master").appendChild(box);
}
else eo.target.style.backgroundColor = "white";
}
The class of all the divs is "box" and I just add a new id to every new box. My problem is that the event handler doesn't seem to work for the newly created boxes. How could that be solved?
Many thanks in advance!
box.onclick = clickHandler;
There are more elegant ways, but as that's what you're already doing, there's no harm doing what you're doing, now.
In a different world, you might do something like:
var master = document.querySelector("#master");
master.addEventListener("click", clickHandler);
function clickHandler (e) {
var box = e.target;
var newBox;
var totalBoxes = master.querySelectorAll(".box").length;
if (!box.classList.contains("box")) {
return; // not a box
}
if (isBlack(box)) {
changeColour(box, "white");
} else {
newBox = makeNewBox(totalBoxes + 1);
master.appendChild(newBox);
changeColour(box, "black");
}
}
I don't have to worry about further click-handling beyond that, if all of the boxes are descendants of #master.
makeNewBox here is simply separating the creation of the object from what you actually want to do with it.
You will need to add an onclick event to your newly added box.
box.onclick = clickHandler;
If you create boxes dynamically after the window.onload handler has already run, then you will have to run some additional code on those dynamically created boxes that assigns the click handler to them after they have been created.
function clickHandler(eo){
if(eo.target.style.backgroundColor != "black") {
eo.target.style.backgroundColor = "black";
var box = document.createElement("div");
box.setAttribute("class","box");
// add this line of code to assign the click handler
box.onclick = clickHandler;
box.setAttribute("id",document.getElementsByClassName("box").length++);
document.getElementById("Master").appendChild(box);
}
else eo.target.style.backgroundColor = "white";
}
Or, you can use delegated event handling and handle the events from a common parent that is not dynamically created.
Delegated event handling uses "event bubbling" where events bubble up their parent chain so you can attach a click handler to a common parent and then check e.target in that click handler to see if the click occurred on one of your box elements and then process it one place. In cases of dynamically added content, this can work very well.
Delegated event handling in your code would look something like this:
window.onload = function(){
// put click handler on common box parent and use event bubbling
document.getElementById("Master").addEventListener("click", clickHandler);
}
function clickHandler(eo){
// if this click occurred on one of my boxes
if (hasClass(eo.target, "box"))
if(eo.target.style.backgroundColor != "black") {
eo.target.style.backgroundColor = "black";
var box = document.createElement("div");
box.setAttribute("class","box");
box.setAttribute("id",document.getElementsByClassName("box").length++);
document.getElementById("Master").appendChild(box);
}
else eo.target.style.backgroundColor = "white";
}
}
// utility function for checking a class name
// could also use .classList with a polyfill for older browsers
function hasClass(elem, cls) {
var str = " " + elem.className + " ";
var testCls = " " + cls + " ";
return(str.indexOf(testCls) !== -1) ;
}

Click on a span causes blur event which moves the span so that it doesn't register click

I have a span to the right of an input, both with some text in them. The input has whatever default width the browser chooses. The input has a blur handler to turn it into a new span with the same text as was in the blurred input. The span has the reverse operation: a click handler to turn it into a new input with the same text as was in the clicked span.
When the span to the right of the input is clicked, the input registers it's blur event and becomes a span, as designed. However it also becomes smaller (assuming not a lot of text) which is also desired. This makes the span that was clicked move over to the left.
The problem: The original span that we clicked now may not be under the mouse pointer and no longer registers the click.
The HTML:
<input id="write" class="tag" value="stuff"></input>
<span id="read" class="tag">some text</span>
The js:
var write = document.getElementById("write");
var read = document.getElementById("read");
var writeOnBlur = function() {
var newRead = document.createElement("span");
newRead.className = "tag";
newRead.innerHTML = this.value;
newRead.onclick = readOnClick;
this.parentNode.replaceChild(newRead, this);
newRead.focus();
}
var readOnClick = function(e) {
alert("clicked the 'read' node");
var newWrite = document.createElement("input");
newWrite.className = "tag";
newWrite.value = this.innerHTML;
newWrite.onblur = writeOnBlur;
this.parentNode.replaceChild(newWrite, this);
newWrite.focus();
e.stopPropagation();
}
document.onclick = function() {
alert("missed the read node. clicked the document.");
}
read.onclick = readOnClick;
write.onblur = writeOnBlur;
write.focus();
See this fiddle: http://jsfiddle.net/7s7kbvvf/10/
Click the span that contains "some text" to see the problem.
Updated to Javascript based solution for what you want:
var switchToInput = function () {
var input = document.createElement("input");
input.className = "tag";
input.type = "text";
input.innerHTML = this.value;
this.parentNode.replaceChild(input, this);
input.onblur = switchToSpan;
input.select();
};
var switchToSpan = function () {
var span = document.createElement("span");
span.className = "tag";
span.innerHTML = this.value;
this.parentNode.replaceChild(span, this);
span.onclick = switchToInput;
}
var tags = document.getElementsByClassName('tag');
for (var i = 0, limit = tags.length; i < limit; i++) {
tags[i].onclick = switchToInput;
}
updated fiddle: http://jsfiddle.net/7s7kbvvf/9/
I was able to find a decent solution by allowing the parent node (or for simplicity in my example here, the document) handle the events. The document tracks the current "write tag" (input) so that when a "read tag" (span) is clicked, it can first replace the span with a new input and then the current input with a new span, both in one click. Since the span is replaced with a new input first, it doesn't matter that it moves once the existing input turns into a new span.
The new js (or see this fiddle):
var write = document.getElementById("write");
var read = document.getElementById("read");
var currWrite = write;
write.focus();
document.onclick = function(e) {
var target = e.target;
if (target && target.nodeName === "SPAN") {
var newWrite = document.createElement("input");
newWrite.className = "tag";
newWrite.value = target.innerHTML;
target.parentNode.replaceChild(newWrite, target);
}
if (currWrite !== null) {
var newRead = document.createElement("span");
newRead.className = "tag";
newRead.innerHTML = currWrite.value;
currWrite.parentNode.replaceChild(newRead, currWrite);
currWrite = null;
}
if (newWrite) {
currWrite = newWrite;
newWrite.focus();
}
}

Preventing mouse click event from firing on elements on a particular z-index

Is there any way to disable onclick events from firing for events on a particular z-index, besides running through all elements and setting their onclick to function(){} if they are on that z-index?
Edit:
At this point, the best I can come up with is to hook each function in the DOM tree recursively:
function preventZIndexClicks(elem) {
// ensure not a text node
if(!elem.popupflag) { elem.popupflag = 1;
if(elem.onclick) { var temp = elem.onclick;
elem.onclick = function(e) {
if(g_threshold > elem.style.zIndex)
return;
temp(e);}
}
}
// Call recusively on elem.childNodes
}
Of course, then I would have to deal with the rather annoying IE issues with setting custom properties on DOM elements...
You could check the z-index in the event and let it bubble through the rest.
function onclick(e) {
if(this.style.zIndex == 10) return;
//do stuff
}
EDIT:
Just to clarify how i mean, consider this:
<div id="div1" style="background-color: red;width:100px;height:100px;z-index:1">
<div id="div2" style="background-color: green;width:50px;height:50px;z-index:2">
<div id="div3" style="background-color: blue;width:25px;height:25px;z-index:3">
</div>
</div>
</div>
With this javascript:
var div1 = document.getElementById("div1");
var div2 = document.getElementById("div2");
var div3 = document.getElementById("div3");
bind(div1,"click",click);
bind(div2,"click",click);
bind(div3,"click",click);
function click(e) {
if(this.style.zIndex == 2) return;
alert(this.style.backgroundColor);
}
function bind(element,event,callback){
var onevent="on"+event;
if(element.addEventListener)
element.addEventListener(event,callback,false);
else if(element.attachEvent)
element.attachEvent(onevent,callback);
else{
var e=element[onevent];
element[onevent]=function(){
var h=e.apply(this,arguments),cbk=callback.apply(this,arguments);
return h==undefined?cbk:(cbk==undefined?h:cbk&&h);
}
}
}
Now, the click will work as follow:
click red: -> alert "red"
click green: -> alert "red"
click blue: -> alert "blue" -> alert "red"
As you see the green element with z-index:2; will not "fire" the event
What about something like:
<script type="text/javascript">
window.onclick = function(e)
{
var targetElem;
if (!e)
{
var e = window.event;
}
if (e.target)
{
targetElem = e.target;
}
else if (e.srcElement)
{
targetElem = e.srcElement;
}
if (targetElem.nodeType == document.TEXT_NODE)
{
targetElem = targetElem.parentNode;
}
if (targetElem.style.zIndex == 100)
{
// do stuff
}
};
</script>
with jQuery:
$('*').each(function() {
var $this = $(this);
if ($this.css('z-index') < g_threshold) {
$this.unbind('click'); //unbinds all click handlers from a DOM element
$this.click(function(e) {
e.stopPropagation();
});
}
});
this is basically like what you do, but requires no additional attributes or whatever else you dislike. If you need to leave, say, textbox untouched, then use $('*').not('input:text')

Categories

Resources