JS not executing on page loaded with .load - javascript

I'm working on a single page application website. All the navigation happens inside a div. I use addEventListener click to find the link, prevent regular href and open it inside the div.
With this code I can change pages, keeping the head and footer without refresh the entire page.
var spa_array = Array.from(document.getElementsByClassName('spa')); // Array with history (for the back button)
spa_array.forEach( function(b) {
var id = b.id;
b.addEventListener('click', function(e) {
event.preventDefault(); // Prevent href
history.pushState({id:id}, null, id); // Change url on browser
openPage(id);
});
});
window.addEventListener('popstate', function(e) {
openPage(e.state.id);
});
function openPage(url) {
var encodedUrl = encodeURI(url);
$("#spa").load(encodedUrl); // open link inside the div
}
This is where the problem happens: I use JS to load a new page on the div, index_2.html for example. The links on index_2.html page will not trigger the JS addEventListener, so it will refresh the page and open the new link.
JS is not being applied to the page opened inside the .load. Is there a way to apply the same script, without calling it again (which causes many a lot of trouble)?

Use a delegated event handler:
$("#spa").on('click', '.spa', function(e){
var anchor = this,
id = anchor.id;
e.preventDefault();
history.pushState({id:id}, null, id);
openPage(id);
});
You can register the handler on a DOM node that does not get removed (in your case, the container whose contents change, but the element itself stays the same), and pass in a selector as a second argument to check if the click occurred on an element inside it that matches the selector.
This is necessary because once you .load() additional content it will replace whatever was there in the element before, including any event listeners that you attached, once, on first load. You'd need to call your loop each time the content is changed, but this is a much less resource intensive solution that will save you the hassle.

I would extract the logic for adding the event listeners as shown below:
function bindLinks() {
var spa_array = Array.from(document.getElementsByClassName('spa'));
spa_array.forEach( function(b) {
var id = b.id;
b.addEventListener('click', function(e) {
event.preventDefault(); // Prevent href
history.pushState({id:id}, null, id); // Change url on browser
openPage(id);
});
});
}
That way you can call bindLinks() after the html is loaded into your div. You can do that by passing it as the second argument to .load().
function openPage(url) {
var encodedUrl = encodeURI(url);
$("#spa").load(encodedUrl, bindLinks);
}

Related

EventListener not working with anchor tel:XXXXXXXXX

I have a tag with href="tel:XXXXXXXXX", and I need catch the click event.
I have tested the following code on chrome: $(document).on('click',console.log). If i click on this tag browser it calls the application, but does not trigger a click event.
$("a[href^='tel']").on('click', console.log);
This is working, but I my have a problem with content load by ajax. My code has loaded a page and after some time application added content by ajax. When i use $(document).on('click', ("a[href^='tel']", console.log), there is a problem.
$("a[href^='tel']").on("click", function(e){
e.preventDefault(); e.stopPropagation();
console.log(this);
alert(this.getAttribute("href"));
})
//or if you want to delegate your function
$(document).on('click', "a[href^='tel']", function(e){
e.preventDefault(); e.stopPropagation();
console.log(this);
alert(this.getAttribute("href"));
});
This will bind an event listener to all click on a tags with a href attribute and prevent the click itself. After click, you'll be able to use your console to see which element was click and what href was used.
Ok, i found resolve.
I use earlier event "mousedown" and change attr "href" to "only number" for disable action click.
Code:
const click2dial_Event = function(e) {
e.preventDefault();
let a = $(this), number;
if (a.attr('href') !== '#') {
number = a.attr('href');
number = number.substr(4);
a.attr('href', '#');
a.attr('data-dial', number)
a.addClass('click-to-dial');
} else {
number = a.attr('data-dial');
}
//...
};
$(document).on('mousedown', "a[href^='tel']", click2dial_Event);
$(document).on('mousedown', '.click-to-dial', click2dial_Event);
This would get the phone number from the a tag starting with a value of tel upon clicking it.
$("a[href^='tel']").on("click", function(e) {
var hrefText = this.getAttribute("href");
var str = hrefText;
var res = str.split(":");
alert(res[1]);
});
On Initial Load
I would first recommend that you wait for the initial DOM to be ready before binding any events to elements.
// DOM ready shorthand
$(function() {
$("a[href^='tel']").on('click', function(e) {
// Do work here
});
});
AJAX Content
If you are adding additional elements after the initial load you will have to bind events to those new elements as well.
You could also do something like adding a data attribute to the elements that you've bound click events to and only add to ones that don't yet have that data attribute - but that's additional unnecessary work.
Full Example Code
// DOM Ready Shorthand
$(function() {
// Click Handler
function clickEvent(e) {
// Do work here
}
// Bind click event to initial tels
$("a[href^='tel']").on('click', clickEvent);
// Arbitrary AJAX request for demonstration (wherever yours may be)
$.ajax('/newContent')
.done(function(html) {
// Adding our response HTML to the page within #someElement
$('#someElement').append(html);
// Bind click event to the new tel elements scoped to the returned html
$("a[href^='tel']", html).on('click', clickEvent);
});
});

Document click not working on dynamic element

Content script:
var $ = window.$.noConflict(true); // Required for IE
function startFunc() {
$('a').mouseover(function(e){
var anchor=this;
var href=$(anchor).attr('href');
if(href!='#'){
$('.klik-me').remove();
const xPos=e.pageX-20;
const yPos=e.pageY-20;
let $klikMe=$('<span class="klik-me">Click Me!!</span>').css({
'padding':'5px',
'background':'#000',
'color':'#FFF',
'font-size':'12px',
'position':'static',
'top':yPos,
'left':xPos,
'text-align':'center',
'z-index':999999
});
$(anchor).append($klikMe);
}
});
}
$('body').on('click','.klik-me',function(){
const href_in=$(this).parent().attr('href');
kango.console.log(href_in);
kango.dispatchMessage('storeHref', {href:href_in});
});
kango.addMessageListener('hrefSuccess', function(event) {
kango.console.log(event.data.link);
});
Background Script:
kango.addMessageListener('storeHref', function(event) {
event.target.dispatchMessage('hrefSuccess', {link:event.data.href});
});
I am adding a pop up for all anchor tags on the page (this is working fine),i added a click event in Jquery(I love this) and using kango.dispatchMessage for sending message to background script. Nothing seems to be working.
Any help would be appreciated.
PS: i use to work with crossrider(Awesome) framework previously.
What I think is happening is the click event is bubbling up to your <a> element and triggering the default navigation action.
Stopping bubbling / default behaviour in delegated event handling is difficult. What I would do instead, is remove the <span> as a child of the <a>. For example
let $klikMe = $('<span class="klik-me">Click Me!!</span>')
.css({ ... })
.data('href', href) // set the href data into the <span>
$(document.body).append($klikMe) // append to body, not anchor
and in your click handler
$(document).on('click', '.klick-me', function() {
const href_in = $(this).data('href')
// then continue as in your original code
return false // the click should have no default action
})
Use the snippet below is what I'm reading everywhere, I can't get it working however.
Currently trying to do somewhat the same: jQuery click event after new page loads
Maybe it can be of some use.
Cheers.
$(parent).on('click', child , callback);

How can I get Iframe event before load?

In my site, I use an iframeA in an iframeB, and, when the iframeA changes it's content I have to set the src. I can set it only with the onload event, but this called when the site is loaded. I am looking for some event or trigger, that helps me detect the location/src change before it starts loading. I don't want to wait the whole page load, before the src set. I have no direct access to iframeA (just the script below)
Some code:
var myframe = document.getElementById('frameB').contentWindow.document.getElementById('frameA');
myframe.onload=function (funcname) {...};
Check this gist or my answer to this question. The code there does exactly that:
function iframeURLChange(iframe, callback) {
var unloadHandler = function () {
// Timeout needed because the URL changes immediately after
// the `unload` event is dispatched.
setTimeout(function () {
callback(iframe.contentWindow.location.href);
}, 0);
};
function attachUnload() {
// Remove the unloadHandler in case it was already attached.
// Otherwise, the change will be dispatched twice.
iframe.contentWindow.removeEventListener("unload", unloadHandler);
iframe.contentWindow.addEventListener("unload", unloadHandler);
}
iframe.addEventListener("load", attachUnload);
attachUnload();
}
It utilizes the unload event. Whenever a page is unloaded, a new one is expected to start loading. If you listen for that event, though, you will get the current URL, not the new one. By adding a timeout with 0 milliseconds delay, and then checking the URL, you get the new iframe URL.
However, that unload listener is removed each time a new page is loaded, so it must be re-added again on each load.
The function takes care of all that, though. To use it, you only have to do:
iframeURLChange(document.getElementById("myframe"), function (url) {
console.log("URL changed:", url);
});
What will be changing the source of the iframe? If you have access to that code then you can do whatever is in your onload function then.
If a link has it's target attribute set to the iframe and that is how the source is changing then you can hi-jack the link clicks:
$('a[target="frameB"]').bind('click', function () {
//run your onload code here, it will run as the iframe is downloading the new content
});
Also, just a side-note, you can bind an event handler for the load event in jQuery like this:
$('#frameB').bind('load', function () {
//run onload code here
});
UPDATE
SITE -> frameB -> frameA
$("#frameB").contents().find("#frameA").bind('load', function () {
//load code here
});
This selects the #frameB element (that is in the current top level DOM), gets it's contents, finds the #frameA element, and then binds an event handler for the load event.
Note that this code must be run after #frameB is loaded with the #frameA element already present in it's DOM. Something like this might be a good idea:
$('#frameB').bind('load', function () {
$(this).contents().find('#frameA').bind('load', function () {
//run load code here
});
});
UPDATE
To hi-jack links in the #frameB element:
$('#frameB').contents().find('a[target="frameA"]').bind('click', function () {
/*run your code here*/
});
This will find any link in the #frameB element that has its target attribute set to frameA and add a click event handler.
And again, this will only work if the #frameB iframe element has loaded (or atleast gotten to the document.ready event) so you can select it's elements.
You could also try taking the approach of detecting when your iframe is going to leave its current location. This may be useful in some situations. To do this, put the following code in you iFarme source.
$(window).on('beforeunload', function () {
alert('before load ...');
});
I think adding inline onload attribute with appropriate event handler to iframe tag will solve your problem.
function onIframeLoad(){
//Write your code here
}
Markup change
<iframe src='..' onload='onIframeLoad()' />

How to change the src and reload an iframe with DOJO?

I want to click on a link, pull the src of the link, and use that src to refresh an iframe in my page.
dojo.ready(function() {
dojo.query(".link").onclick(function(e) {
var node = e.target;
var url = dojo.attr(node, "href");
document.getElementById("audioiframe").src = url;
dojo.stopEvent(e);
});
});
I could do this quickly in OG Javascript or jQuery, but need to use DOJO.
Thank you
UPDATE: After incorporating Strife25 answer my code is:
dojo.ready(function() {
dojo.query(".audiolink").forEach(function(link){
dojo.connect(link, "onclick", null, function(e){
var node = e.target;
var url = dojo.attr(node, "href");
dojo.query("#audioiframe").attr("src", url);
dojo.stopEvent(e);
});
});
});
The event is attached to the links, the iframe src attribute is updated, but the onclick event is not canceled and the page behaves as if the link is clicked. It appears as if dojo.stopEvent(e); is not working (asking where to download the MP3 File). Also, the iframe is not reloading but I believe that is due to the click not being canceled.
UPDATE:
I am the bug, not the code! I was linking to the MP3 versus the page that housed the MP3 (long story). It works. Thank you.
You're attaching the click event in the wrong way, you should use dojo.connect to attach event handlers. Also, dojo.query() returns a NodeList when it runs instead of a single node.
The proper way to use dojo in this instance would be:
dojo.query(".link").forEach(function(link){
dojo.connect(link, "onclick", null, function(e){
//do your stuff
});
});
If you only have one button that performs the onclick event you are proposing, you should instead use dojo.byId like so:
var link = dojo.byId("linkId"); //retrieves a reference to a DOM node with the given id
dojo.connect(link, "onclick", null, function(e){
//do you stuff
});
Here is the doc page of dojo.connect, which also explains how you can clean up event handlers on object destruction / page unload: http://www.dojotoolkit.org/reference-guide/dojo/connect.html

preventing effects to be applied to the same object twice when adding objects with ajax

I'm having a little issue with an application I'm making. I have a page where the user edits a document via dragging modules into the page or "canvas" area.
http://thinktankdesign.ca/temp_img.jpg
When the page is loaded, javascript haves the modules collapsible (like above). However after the user drags in a new module the effect is applied again some new modules can collapse as well. here is the problem. each time a module loads the same effect gets applied to the modules that already can collapse. It ends up breaking their animations.
heres the code that gets executed on page load.
//make colapsible
$("h1.handle").click(function() {
var object = $(this);
v$(this).next().toggle("fast", colapsible_class(object));
vreturn false;
}).addClass("open");
and heres the code that gets executed in the creation of a module via ajax
function get_module(id){
var template = $('input[name=template]').val();
$.post(window.location.href, { template: template, module: id, mode: 'create' },
function(data){
$(data).insertBefore(".target_wrapper");
//enable deletion of module
$(".js_no_modules").slideUp("slow");
$(enable_module_deletion());
//show delete button
$("button[name=delete]").show();
//make colapsible
$("h1.handle").click(function() {
var object = $(this);
$(this).next().toggle("fast", colapsible_class(object));
return false;
}).addClass("open");
}
);
}
I need a solid way of preventing the toggle effect to be applied to the same module twice
Use jQuery 1.3 live events instead.
//make colapsible
$("h1.handle").live("click", function() {
var object = $(this);
v$(this).next().toggle("fast", colapsible_class(object));
vreturn false;
}).addClass("open");
and then eliminate the click declaration in the second block of code, changing it to $("h1.handle").addClass("open");
Live events bind all current and future matching elements with an event.
In your Ajax success handler try the following:
//make collapsible
$("h1.handle:not(.open)").click(function() {
var object = $(this);
$(this).next().toggle("fast", colapsible_class(object));
return false;
}).addClass("open");
The best way to solve your problem is, instead of using $("h1.handle") on the AJAX callback, go for $(data).find("h1.handle"). Something like,
var x = $(data);
x.insertBefore(...);
/* your other code */
x.find('h1.handle').click(...).addClass(...);
Like that, only the newly added items will have the event bounded. The already present ones will not be touched.
If we want to answer your question instead of just solving your problem, then we have several alternatives, such as:
store, in your objects, that the onclick event handler has been set so that you don't set it twice
always bind the onclick event, but always unbind it first
use jQuery's live events and the addClass open only on the newly created items.
IMO, the first one is the easiest. You can accomplish it by using jQuery's data(). Then you could do something like:
$("h1.handle").each(function() {
var me = $(this);
// if already has click handler, don't do anything
if (me.data('click_set') != null) { return true; }
// otherwise, store the data and bind the click event
me.data('click_set', true).click(function() {
/* the code you already have on the click handler */
}).addClass('open');
}
The second alternative involves storing the function that you pass inline to the click event binder in a variable, and then using jQuery's unbind to disable it.

Categories