I want website to change class of an element if it is filled. So, when user blurs out of an input field the program checks if it has any value and if yes adds a class. The problem is to pass this behaviour to each element in class' collection.
var input = document.getElementsByClassName('input');
contentCheck = function(i){
if(input[i].value>0) input[i].classList.add('filled');
else input[i].classList.remove('filled');
};
for(var i=0; i<input.length; i++) {
input[i].addEventListener('blur',contentCheck(i));
}
This works once after reloading the page (if there's any content in cache), but contentCheck() should trigger each time you leave the focus.
You've half-applied the "closures in loops" solution to that code, but you don't need the closures in loops solution, just use this within contentCheck and assign it as the handler (rather than calling it and using its return value as the handler):
var input = document.getElementsByClassName('input');
var contentCheck = function(){ // <== No `i` argument (and note the `var`)
// Use `this` here
if(this.value>0) this.classList.add('filled');
else this.classList.remove('filled');
};
for(var i=0; i<input.length; i++) {
input[i].addEventListener('blur',contentCheck);
// No () here -------------------------------------^
}
Side note: classList has a handy toggle function that takes an optional flag:
var contentCheck = function(){
this.classList.toggle('filled', this.value > 0);
};
If you needed the "closures in loops" solution (again, you don't, but just for completeness), you'd have contentCheck return a function that did the check:
var input = document.getElementsByClassName('input');
var makeContentCheckHandler = function(i){
return function() {
if(input[i].value>0) input[i].classList.add('filled');
else input[i].classList.remove('filled');
};
};
for(var i=0; i<input.length; i++) {
input[i].addEventListener('blur', makeContentCheckHandler(i));
}
Note I changed the name for clarity about what it does.
Try to use anonymous function
input[i].addEventListener('blur',function(e){
console.log(e);
});
Example: https://jsfiddle.net/42etb4st/4/
Related
I am trying to write a javascript program which stores the value from an input element in an array when a button is clicked. The array is the split and each individual letter added to a span element and then appended to the document. The idea is to create a typing effect using setTimeout.
I am running into an issue creating a closure within the loop, so currently the setTimeout function always returns the final value of the iteration.
The function in question is at the bottom of the code block and called addTextToBoard();
var noteButton = document.querySelector('[data-js="button"]');
noteButton.addEventListener("click",function() {
var messageIn = document.querySelector('[data-js="input"]');
var message = messageIn.value;
postToBoard(message);
});
function postToBoard(val) {
var noteBoard = document.querySelector('[data-js="noteboard"]');
var newElement = document.createElement('div');
newElement.classList.add('noteboard__item');
noteBoard.appendChild(newElement);
setTimeout(function(){
newElement.classList.add('active');
}, 200);
addTextToBoard(newElement, val);
}
function addTextToBoard(el, val) {
var wordArray = val.split('');
for(i = 0; i < wordArray.length; i++) {
var letter = document.createElement('span');
letter.innerHTML = wordArray[i];
setTimeout(function(x){
return function() {}
el.appendChild(letter);
}(i),1000);
}
}
I believe I am close, I'm just not fully understanding the syntax for creating the closure. If someone could give poke in the right direction, without necessarily giving the full solution that would be great.
I essentially tried to paste in the following code snippet from here but I've missed something somehwere along the way!
setTimeout(function(x) { return function() { console.log(x); }; }(i), 1000*i);
Best,
Jack
You are close.
Since the "letter" variable changes, you'll add only the last letter over and over again. You need to "save" the current letter on the setTimeout() callback function, One way to go is like this:
function appendMyLetter(letter) {
return(function() {
el.append.Child(letter);
});
}
function addTextToBoard(el, val) {
var wordArray = val.split('');
for(i = 0; i < wordArray.length; i++) {
var letter = document.createElement('span');
letter.innerHTML = wordArray[i];
setTimeout(appendMyLetter(letter), 1000);
}
}
This way, the appendMyLetter() function gets called with a different parameter (one for each letter) and returns a function with the correct "stored" value to be called by setTimeout().
EDIT
Looking at your setTimeout() code closely
setTimeout(function(x){
return function() {}
el.appendChild(letter);
}(i),1000);
It would work fine, if you used the proper parameters and used the appendChild() inside the returned function, like so:
setTimeout(function(x){
return(function() {
el.appendChild(x);
});
}(letter),1000);
You can create an immediately-invoked function expression IIFE to create a closure
function addTextToBoard(el, val) {
var wordArray = val.split('');
for(i = 0; i < wordArray.length; i++) {
(function(index) {
var letter = document.createElement('span');
letter.innerHTML = wordArray[i];
setTimeout(function(){
el.appendChild(letter);
},1000);
})(i);
}
}
I dont know if this will work but here you go a slight change in operator:
letter.innerHTML += wordArray[i];
if you dont get the effect you imagined you will get you better try to increment the timer by i like this
setTimeout(function(){
...
},1000*i);
No jQuery please!
I would love not to use jQuery for this, because it's a big library and I only need to do this single thing:
I would like to add a short delay to a click event so I can fade elements off my page using CSS.
I could post all of the code I've tried so far, but you'd get bored. So here's the one that I think is the closest:
document.getElementsByTagName('a').onclick = function (e) {
// Delay setting the location for one second
setTimeout(function() {this.href}, 90000);
var animOut = document.getElementByClassName('animateOut');
animOut.className += 'out';
};
I've already ruled out using onbeforeunload, so hijacking the click event seems to be the best way.
Once again, I know this would be a doddle in jQuery, but I would like to avoid it if it's possible.
Thanks so much for your answers.
Ben
UPDATE
Thanks to commenters guest271314 and The Alpha, I've settled on this approach, but still have yet to complete the puzzle.
window.onload = function(){
var links = document.getElementsByTagName('a');
for( var i=0,il = links.length; i< il; i ++ ){
links[i].onclick = clickHandler;
}
function clickHandler(event) {
event.preventDefault();
// Delay setting the location for one second
setTimeout(function() {
location.href = this.href;
}, 90000);
// add `s` to `Element`
var animOut = document.getElementsByClassName("animateOut");
// iterate `animOut` elements
for (var i = 0; i < animOut.length; i++) {
// add `out` `className` to `animOut` element at index `i`
animOut[i].classList.add("out");
};
};
};
Having to iterate over the a tags was an addition I have learned from reading other posts (I'll link to them in a minute).
Unfortunately, the setTimeout function doesn't seem to be working. I need to refine what's in this function, but don't know in what way.
Further assistance would be most welcome.
Thanks
Ben
I can't really take credit for this, the 2 users below (guest271314 and The Alpha) deserve a lot of recognition for what they helped me to achieve. The full code, with a couple of refinements, is:
window.onload = function(){
var links = document.getElementsByTagName('a');
for( var i=0,il = links.length; i< il; i ++ ){
links[i].onclick = clickHandler;
}
function clickHandler(event) {
event.preventDefault();
var travelTo = this.getAttribute("href");
// add `s` to `Element`
var animOut = document.getElementsByClassName("animateOut");
// iterate `animOut` elements
for (var i = 0; i < animOut.length; i++) {
// add `out` `className` to `animOut` element at index `i`
animOut[i].classList.add("out");
};
// Delay page out until the animation finishes
setTimeout(function() {
window.location.href = travelTo;
}, 1000);
};
};
You may try something like this:
document.getElementsByTagName('a').onclick = function (event) {
event.preventDefault();
document.getElementByClassName('animateOut').className += ' out';
setTimeout(function() {
location.href = this.href;
}, 1000);
};
There should be an "s" at document.getElementsByClassName . To add className to all animateOut elements can use a for loop; change this.href to window.location.href = e.target.href if expected result is to navigate to href of clicked a element; else leave as this.href is requirement is to refresh current window.location.href : this.href within setTimeout
document.getElementsByTagName("a").onclick = function (e) {
// Delay setting the location for one second
setTimeout(function() {window.location.href = this.href}, 90000);
// add `s` to `Element`
var animOut = document.getElementsByClassName("animateOut");
// iterate `animOut` elements
for (var i = 0; i < animOut.length; i++) {
// add `out` `className` to `animOut` element at index `i`
animOut[i].classList.add("out");
};
I'm trying to add an onclick event handler to a list of checkboxes. Instead of alerting the checkbox ID that I would expect it to (the one I'm clicking on), it's always alerting checkbox number 3 regardless of which on I click. Why is this and how can I get this to behave as desired. When I click on checkbox 2, I want it to alert "2"... etc.
function component (componentId, displayType, parentId) {
this.componentId = componentId;
this.parentId = parentId;
this.displayType = displayType;
}
var components = [];
components.push(new component(0, null, null);
components.push(new component(1, null, null);
components.push(new component(2, null, null);
components.push(new component(3, null, null);
var arrayLength = components.length;
for (var i = 0; i < arrayLength; i++) {
var component = components[i];
jQuery('#questionnaireComponentId'+component.componentId).find('input:checkbox').click(
function(){
selectParentCheckbox(component.componentId);
}
);
}
function selectParentCheckbox(componentId){
alert(componentId);
}
This is happening because of the Javascript closures.
You should pass the parameter each iteration, and not only after finishing the iteration.
for (var i = 0; i < arrayLength; i++) {
var component = components[i];
jQuery('#questionnaireComponentId' + component.componentId)
.find('input:checkbox')
.click(selectParentCheckbox.bind(null, component.componentId));
}
The bind function allows you to name what the function the click event will be calling and what parameters should be passed to it.
Nothing to do with the question, but only for your information: I think you're misunderstanding the power of jQuery and its event delegation, since you could use a much better aproach at all.
Hi I need to valid the href is empty or not on my page using javascript. I searched the site and found some example, but it didn't worked for me. I must miss something that I didn't notice. Would someone point me the good direction and my mistake. I got the error" Unable to get property 'getattribute' of undefined or null reference. The <a> element is like that <a name="playback" href=""> on html file.
Thanks in advance.
There is my code which is run on load event:
var anchors = document.getElementsByTagName("a");
for (var i = 0; i < anchors.length; i++)
{
anchors[i].onclick = function() {
if (anchors == null) {
alert('null');
}
else {
var link = anchors[i].getAttribute("href");
//var link= anchors[i].attributes['href'] this line doesn't work too.
}
}
}
}
In your code, the call to getAttribute is inside a closure (that is, a function defined "inline" without a name) that is assigned to the onlick event handler of the link. Therefore that code isn't execxuted right away - it doesn't run before the onclick handler triggers.
When the onclick header triggers, two things are passed to the callback function: the element on which the event was triggered is assigned to the this variable of the functions context - and the event itself is passed as first parameter. anchors however is undefined in the scope of that callback.
So, use either of those:
anchors[i].onclick = function () {
var link = this.getAtrribute("href");
}
anchors[i].onclick = function (event) {
var link = event.target.getAttribute("href");
}
You have got a scope problem.
The following code will output 3:
for (var i = 0; i < 3; i++) {
}
console.log(i); // 3
Similar to the example above your onclick is fired after the loop is done.
So i in your example would equal to anchors.length.
And anchors[anchors.length] === undefined.
To solve this problem you have to create a new scope.
For example you could use an Immediately-Invoked Function Expression (IIFE):
var anchors = document.getElementsByTagName("a");
for (var i = 0; i < anchors.length; i++)
{
(function(j){
anchors[j].onclick = function() {
if (anchors == null) {
alert('null');
}
else {
var link = anchors[j].getAttribute("href");
}
}
}
}(i));
}
You need to use closure if you want to do it this way since you are using the shared i variable which would have been having last value of iteration when your handler runs on click. But since you are looking at that particular anchor, try binding it with bind an event listener and access it using this.href:
You can use addEventListener and for older browser support attachEvent
var anchors = document.getElementsByTagName("a");
for (var i = 0; i < anchors.length; i++) {
anchors[i].addEventListener('click', function () {
var link = this.getAttribute("href");
})
};
Demo
Or :
var anchors = document.getElementsByTagName("a");
for (var i = 0; i < anchors.length; i++) {
anchors[i].onclick = getHandler(i);
}
function getHandler(i) {
return function () { //Now each of your handler has its own `i`
var link = anchors[i].getAttribute("href");
}
}
Demo
I have never seen getAttribute before so I performed some tests on it. It turns out that href and getAttribute("href") are quite different. Namely href is the absolute url and getAttribute("href") is the relative url to the page. See this fiddle.
The problem with your code is that the var is captured in the closure of onclick and when the onclick function runs the value will of i will be anchors.length.
Solution, Scratch that use the code from Johannes H. His is better
var anchors = document.getElementsByTagName("a");
for (var i = 0; i < anchors.length; i++) {
(function () {
var current = anchors[i]; //Capture the anchor element
current.onclick = function() {
var link = current.getAttribute("href");
};
} ());
}
See this w3 schools page for how to get the href attribute from anchor tags.
http://www.w3schools.com/jsref/prop_anchor_href.asp
var anchors = document.getElementsByTagName("a");
for (var i = 0; i < anchors.length; i++)
{
anchors[i].onclick = function() {
if (anchors == null) {
alert('null');
}
else {
var link = this.href;
}
}
}
I have to call another function before the original onclick event fires, I've tried a lot of different paths before I've come to following solution:
function bindEnableFieldToAllLinks() {
var links = document.getElementsByTagName('a');
for (var i = 0; i < links.length; i++) {
var link = links[i];
var onclick = link.getAttribute('onclick');
link.onclick = new Function("if(linkClickHandler()){"+onclick+"}");
console.log(link.getAttribute('onclick'));
}
}
This does the trick in Firefox and Chrome but IE8 is acting strange, it seems that the function that's in the onclick variable isn't executed.
I've already added console.log messages that get fired after the if statement is true and if I print out the onclick attribute I get following:
LOG: function anonymous() {
if(linkClickHandler()){function onclick()
{
if(typeof jsfcljs == 'function'){jsfcljs(document.getElementById('hoedanigheidForm'), {'hoedanigheidForm:j_id_jsp_443872799_27':'hoedanigheidForm:j_id_jsp_443872799_27'},'');}return false
}}
}
So it seems that the function is on the onclick of the link and the old onclick function is on it as well.
Can anyone help me out with this please?
Say you have an onclick attribute on a HTMLElement..
<span id="foo" onclick="bar"></span>
Now,
var node = document.getElementById('foo');
node.getAttribute('onclick'); // String "bar"
node.onclick; // function onclick(event) {bar}
The latter looks more useful to what you're trying to achieve as using it still has it's original scope and you don't have to re-evaluate code with Function.
function bindEnableFieldToAllLinks() {
var links = document.getElementsByTagName('a'),
i;
for (i = 0; i < links.length; i++) function (link, click) { // scope these
link.onclick = function () { // this function literal has access to
if (linkClickHandler()) // variables in scope so you can re-
return click.apply(this, arguments); // invoke in context
};
}(links[i], links[i].onclick); // pass link and function to scope
}
Further, setting a named function inside an onclick attribute (i.e. as a String) doesn't achieve anything; the function doesn't invoke or even enter the global namespace because it gets wrapped.
Setting an anonymous one is worse and will throw a SyntaxError when onclick tries to execute.
This will do what you want, executing what is inside linkClickHandler first, and then executing the onclick event. I put in a basic cross browser event subscribing function for your reuse.
bindEnableFieldToAllLinks();
function bindEnableFieldToAllLinks() {
var links = document.getElementsByTagName('a');
for (var i = 0; i < links.length; i++) {
var link = links[i];
var onclick = link.getAttribute('onclick');
onEvent(link, 'click', function() {
linkClickHandler(onclick);
});
link.onclick = undefined;
}
}
function onEvent(obj, name, func) {
if (obj.attachEvent) obj.attachEvent('on' + name, func);
else if (obj.addEventListener) obj.addEventListener(name, func);
}
function linkClickHandler(funcText) {
alert('before');
var f = Function(funcText);
f();
return true;
}
jsFiddle