addEventListener only firing once - javascript

I'm adding a click event to all links that match a particular selector as part of a JS module I'm creating. It looks something like this.
var Lightbox = (function () {
var showLightbox = function () {
// this does stuff
};
var init = function () {
var links = document.querySelectorAll(options.selector);
for(var i = 0; i < links.length; i++) {
links[i].addEventListener('click', function() {
showLightbox();
}, false);
}
};
return {
init: init
};
})();
Lightbox.init();
On first load the any links on the page that match the selector work. There is also a closeLightbox() method that works fine. However when clicking the links for a second time nothing happens. I get no console errors – nuffin.
Is there something I'm doing wrong when adding the event listener?
EDIT: I've updated the code to remove some redundant methods and have pasted the full code here: http://pastebin.com/mC8pSAV2

You are reassigning innerHTML of the whole document:
document.body.innerHTML += response;
on the link click. That wipes out all existing DOM elements with their events and creates new DOM structure with no clicks assigned.

Related

How to show the result of this jQuery function without the need of clicking the button?

I have this function below, however I want to make it work on windows load and show the result without clicking the button.
This is the code I use https://raw.githubusercontent.com/SuyashMShepHertz/indexedDB_sample/master/index.html
How to do this?
$("#getBtn").click(function(){
var type = 'permanent';
var request = db.transaction(["hashes"],"readwrite").objectStore("hashes").get(type);
request.onsuccess = function(event){
$("#result").html("Name : "+request.result.name);
};
});
just put your code in
$( window ).load(function() {
//Code Here
});
If you need it both on click and initially when the page loads, make it a reusable function:
function doTheThing() {
var type = 'permanent';
var request = db.transaction(["hashes"], "readwrite").objectStore("hashes").get(type);
request.onsuccess = function(event) {
$("#result").html("Name : " + request.result.name);
};
}
Then call it from both places you need it:
On page load
On click
To call it on page load, just make sure your script is at the end of the HTML (just before the closing </body> tag; this is best practice unless you have a good reason for doing something else) and call it:
doTheThing();
If you can't put the script at the end of the HTML, you can use jQuery's ready callback instead:
// Concise, but easy to misunderstand:
$(doTheThing);
// Or more verbose but also more clear:
$(document).ready(doTheThing);
(See note below about doing it directly or indirectly.)
To call it on click, hook it up, either directly or indirectly:
// Directly
$("#getBtn").click(doTheThing);
// Or indirectly
$("#getBtn").click(function() {
doTheThing();
});
The only reason for hooking it up indirectly would be to avoid having it receive the event object jQuery will pass it automatically, and to avoid having its return value examined by jQuery to see if it should stop propagation and prevent the default event action.
To avoid creating globals, I'd make sure the entire thing is in a scoping function:
(function() {
function doTheThing() {
var type = 'permanent';
var request = db.transaction(["hashes"], "readwrite").objectStore("hashes").get(type);
request.onsuccess = function(event) {
$("#result").html("Name : " + request.result.name);
};
}
doTheThing();
$("#getBtn").click(doTheThing);
})();
just put it in $(document).ready, like this
$(document).ready(function(){
var type = 'permanent';
var request = db.transaction(["hashes"],"readwrite").objectStore("hashes").get(type);
request.onsuccess = function(event){
$("#result").html("Name : "+request.result.name);
};
});

listening for click event for an href by classname

there is a page with some basic HTML that I cannot touch that looks like this:
<a class="continue-shopping" href="https://someURL">Continue shopping</a>
what I want to do is send the user to a different link when they click on the someURL text link. the user can come to a page containing this html from many other pages.
i have tried many hours but cannot get my js to recognize a click event for a class associated with hyperlinked text. i could really use some help here. this is the js code i wrote which does not work
window.onload = function() {
prepEventHandler();
}
function prepEventHandler () {
var myClass = document.getElementsByClassName("continue-shopping");
myClass[0].onclick=window.open(document.referrer,"_self");
/* which make my pages go haywire OR THIS -- which also does not work */
myClass[0].addEventListener("click", function() {
window.open(document.referrer,"_self");
}
)
}
It just keeps ignoring the second function, and I am sure I am doing some really basic that is wrong. Again, thanks for any help!
Apart from preventDefault() you could also use return false
window.onload = function () {
var myClass = document.querySelector(".continue-shopping")
.onclick = function () {
window.location.href = "http://elsewere.com";
return false;
}
}
this code should work but it no longer does and i do not know why any hint much appreciated - there seems to be some problem with myClass[0]
window.onload = function() {
var myClass = document.getElementsByClassName('continue-shopping');
myClass[0].addEventListener("click", function(e) {
e.preventDefault();
window.location.href = document.referrer;
});
}

Click all anchor tags on page with given class, but cancel prior to navigation

Trying to automate some testing for some analytics tracking code, and I'm running into issues when I try passing links into the each() method.
I copied a lot of this from stackoverflow - how to follow all links in casperjs, but I don't need return the href of the link; I need to return the link itself (so I can click it). I keep getting this error: each() only works with arrays. Am I not returning an array?
UPDATE:
For each anchor tag that has .myClass, click it, then return requested parameters from casper.options.onResourceReceived e.g. event category, event action, etc. I may or may not have to cancel the navigation the happens after the click; I simply only need to review the request, and do not need the follow page to load.
Testing steps:
click link that has .myClass
look at request parameters
cancel the click to prevent it from going to the next page.
I'm new to javascript and casper.js, so I apologize if I'm misinterpreting.
ANOTHER UPDATE:
I've updated the code to instead return an array of classes. There are a few sketchy bits of code in this though (see comments inline).
However, I'm now having issues canceling the navigation after the click. .Clear() canceled all js. Anyway to prevent default action happening after click? Like e.preventDefault();?
var casper = require('casper').create({
verbose: true,
logLevel: 'debug'
});
casper.options.onResourceReceived = function(arg1, response) {
if (response.url.indexOf('t=event') > -1) {
var query = decodeURI(response.url);
var data = query.split('&');
var result = {};
for (var i = 0; i < data.length; i++) {
var item = data[i].split('=');
result[item[0]] = item[1];
}
console.log('EVENT CATEGORY = ' + result.ec + '\n' +
'EVENT ACTION = ' + result.ea + '\n' +
'EVENT LABEL = ' + decodeURIComponent(result.el) + '\n' +
'REQUEST STATUS = ' + response.status
);
}
};
var links;
//var myClass = '.myClass';
casper.start('http://www.leupold.com', function getLinks() {
links = this.evaluate(function() {
var links = document.querySelectorAll('.myClass');
// having issues when I attempted to pass in myClass var.
links = Array.prototype.map.call(links, function(link) {
// seems like a sketchy way to get a class. what happens if there are multiple classes?
return link.getAttribute('class');
});
return links;
});
});
casper.waitForSelector('.myClass', function() {
this.echo('selector is here');
//this.echo(this.getCurrentUrl());
//this.echo(JSON.stringify(links));
this.each(links, function(self, link) {
self.echo('this is a class : ' + link);
// again this is horrible
self.click('.' + link);
});
});
casper.run(function() {
this.exit();
});
There are two problems that you're dealing with.
1. Select elements based on class
Usually a class is used multiple times. So when you first select elements based on this class, you will get elements that have that class, but it is not guaranteed that this will be unique. See for example this selection of element that you may select by .myClass:
myClass
myClass myClass2
myClass myClass3
myClass
myClass myClass3
When you later iterate over those class names, you've got a problem, because 4 and 5 can never be clicked using casper.click("." + links[i].replace(" ", ".")) (you need to additionally replace spaces with dots). casper.click only clicks the first occurrence of the specific selector. That is why I used createXPathFromElement taken from stijn de ryck to find the unique XPath expression for every element inside the page context.
You can then click the correct element via the unique XPath like this
casper.click(x(xpathFromPageContext[i]));
2. Cancelling navigation
This may depend on what your page actually is.
Note: I use the casper.test property which is the Tester module. You get access to it by invoking casper like this: casperjs test script.js.
Note: There is also the casper.waitForResource function. Have a look at it.
2.1 Web 1.0
When a click means a new page will be loaded, you may add an event handler to the page.resource.requested event. You can then abort() the request without resetting the page back to the startURL.
var resourceAborted = false;
casper.on('page.resource.requested', function(requestData, request){
if (requestData.url.match(/someURLMatching/)) {
// you can also check requestData.headers which is an array of objects:
// [{name: "header name", value: "some value"}]
casper.test.pass("resource passed");
} else {
casper.test.fail("resource failed");
}
if (requestData.url != startURL) {
request.abort();
}
resourceAborted = true;
});
and in the test flow:
casper.each(links, function(self, link){
self.thenClick(x(link));
self.waitFor(function check(){
return resourceAborted;
});
self.then(function(){
resourceAborted = false; // reset state
});
});
2.2 Single page application
There may be so many event handlers attached, that it is quite hard to prevent them all. An easier way (at least for me) is to
get all the unique element paths,
iterate over the list and do every time the following:
Open the original page again (basically a reset for every link)
do the click on the current XPath
This is basically what I do in this answer.
Since single page apps don't load pages. The navigation.requested and page.resource.requested will not be triggered. You need the resource.requested event if you want to check some API call:
var clickPassed = -1;
casper.on('resource.requested', function(requestData, request){
if (requestData.url.match(/someURLMatching/)) {
// you can also check requestData.headers which is an array of objects:
// [{name: "header name", value: "some value"}]
clickPassed = true;
} else {
clickPassed = false;
}
});
and in the test flow:
casper.each(links, function(self, link){
self.thenOpen(startURL);
self.thenClick(x(link));
self.waitFor(function check(){
return clickPassed !== -1;
}, function then(){
casper.test.assert(clickPassed);
clickPassed = -1;
}, function onTimeout(){
casper.test.fail("Resource timeout");
});
});

Track onclick events

My goal is to store the total amount of clicks on links in a certain page into a variable and recall that variable when the user exits the page. Would this be a correct way of doing it?
window.onunload = function(){
alert("Total Links Clicked:" + clicks(););
}
var clicks = function(){
var clickArray = [];
var getLinks = document.getElementsByTagName(A);
var clickValue = getLinks.onclick = function() {
clickArray.push("1");
}
var totalClicks = clickArray.length;
return totalClicks;
}
Your code won't work for several reasons:
You bind the click handler in clicks() function and don't call clicks() until the page is unloaded, at which point it is too late to handle any clicks. You need to bind the click handler when the page loads.
You can't set .onclick on a list of elements, which is what your getLinks variable is given it was set to the result of getElementsByTagName() (get elements not get element).
You pass an undeclared variable A to getElementsByTagName(); you should pass the string "a".
You have a semicolon inside the alert()'s closing ), which is a syntax error.
You could try something like this:
window.onload = function() {
var clicks = 0;
window.onunload = function(){
alert("Total Links Clicked:" + clicks);
}
function clicked() {
clicks++;
}
var getLinks = document.getElementsByTagName("a");
for (var i = 0; i < getLinks.length; i++)
getLinks[i].onclick = clicked;
};
Note that the browser may block an alert() during an unload event, but if you use console.log() instead you can see that clicks had the right value.
Note also that of course a click on a link to another page will cause an unload, so the click count will only be more than 1 if you have links within the current page.
Demo: http://jsfiddle.net/A3bkT/1/
You can use sessionStorage or localStorage for that.
if(typeof(Storage)!=="undefined")
{
// store the clicks here
sessionStorage.numClicks = value;
}
and read more on these two here : http://www.w3schools.com/html/html5_webstorage.asp
Bind a global event listener and just store the count with sessionStorage:
var clicks = 0;
document.addEventListener('click', function(event) {
if (event.target.nodeName == 'A') {
window.sessionStorage.setItem('clicks', ++clicks);
}
}, false);

Transform any JavaScript function into a page event

I need to be able to achieve the following (one way or another):
function ShowContent() {}
document.onShowContent = function ()
{
// anything I want to happen....
}
What I'm trying to do is to add a kind of listener to me Advertisement code on the page that will auto refresh the ad slot when a specific function is called. Instead of having that function "ShowContent()" directly refresh the ad code, I want the ad code to refresh if it detects that "ShowContent()" has been called.
Thanks.
Modern javascript libraries make this easy. You can do it "by hand" of course, but here's a quick example with jQuery
First, the listener
$(document).bind( 'ShowContent', function()
{
// anything you want
});
Then the trigger
$(document).trigger( 'ShowContent' );
You could even go this route if you want
function ShowContent()
{
$(document).trigger( 'ShowContent' );
}
Here is a quick sample i threw together
var ev = (function(){
var events = {};
return {
on: function(name, handler){
var listeners = (name in events) ? events[name] : (events[name] = []);
listeners.push(handler);
},
raise: function(name){
var listeners = events[name];
if (listeners) {
var i = listeners.length;
while (i--) {
listeners[i]();
}
}
}
};
})();
// add a listener
ev.on("foo", function(){
alert("bar");
});
If you cannot manually alter the method in question to trigger the event, then you can 'wrap' it.
function methodIHaveNoControlOver(){
....
}
// intercept the call
var originalFn = methodIHaveNoControlOver;
// here we replace the FunctionDeclaration with a FunctionExpression containing a reference to the original FunctionDeclaration
methodIHaveNoControlOver = function(){
originalFn();
ev.raise("foo");
};
But note that this will not work if methodIHaveNoControlOver uses this to reference anything; so that will require more work.

Categories