I will describe my problem, hopefully someone can provide a solution.
I am adding a GreaseMonkey script to an existing page, the page loads with ajax an element with an onclick:
onclick="getProperty('http:/...')"
I searched for a way to override their getProperty, which was hard on itself because I can't know what object will have this onclick before it arrives via ajax.
I can't bind my own click and unbind the previous one because there's no trigger for such a function when the content is dynamic. I tried adding my click and preventing the previous from being called by that doesn't work.
The selector I'm using looks like this:
a[onclick^='getProperty']
What I discovered was a method someone wrote for this exact problem, called waitForKeyElements. I hope someone here is already familiar with it.
What it does is check if an element matching a selector was added by ajax to the page, and if so runs a function.
This method let me workaround binding my function to override theirs:
waitForKeyElements("a[onclick^='getProperty']", updateToNewGetProperty);
function updateToNewGetProperty(ajaxLinks){
ajaxLinks.each(function(){
var oldOnclick = $(this).attr("onclick");
var UrlStartPos = oldOnclick.indexOf('\'') + 1;
var UrlEndPos = oldOnclick.indexOf('\'',UrlStartPos);
var Url = oldOnclick.substring(UrlStartPos,UrlEndPos);
$(this).attr("onclick", ""); // unbind
$(this).click(function() { // bind mine
myGetProperty(Url);
return false;
});
});
}
This works, it unbinds the previous javascript onclick and sets the jquery click.
However then I discovered that another segment of their code grabs the URL value that is inside the onclick, so I can't use the jquery click bind as that leaves the onclick empty.
I had to revert back to:
$(this).attr("onclick",$(this).attr("onclick").replace('getProperty', 'myGetProperty'));
This returns function not defined when clicking the link, I believe because the original page loads the ajax content before the greasemonkey is fully loaded. Triggering the waitForKeyElements before my function is registered.
Any help / advice would be greatly appreciated
Related
I have a Chrome extension that intercepts and checks tweets before they get posted. To do this, I've add an event listener to the Tweet button. Sine the content is dynamic, I use the solution proposed in this thread:
initialize : function() {
let that = this;
let jsInitChecktimer = setInterval(checkForJsFinished, 111);
function checkForJsFinished () {
if (document.querySelector("div[data-testid='tweetButtonInline']")) {
clearInterval (jsInitChecktimer);
console.log("Button found");
that.addSubmitNewTweetClickHandler();
}
}
},
addSubmitNewTweetClickHandler : function() {
let that = this;
let buttonSubmitTweet = document.querySelector("div[data-testid='tweetButtonInline']");
buttonSubmitTweet.addEventListener('click', function(e) {
console.log("CLICK");
// Stop default event from happening
e.preventDefault();
e.stopImmediatePropagation();
// Do stuff
});
},
If the tweet passed the checks alright, it gets submitted by programmatically triggering the event using .trigger('click').
This works fine, but only once. After a tweet has been submitted and posted, the event listener on the Tweet button is gone, and I cannot intercept the next tweet to check it. I've tried calling initialize() after submitted again -- maybe the button gets removed and newly added to the DOM (it actually disappears fire a moment when submitting a tweet) -- but the querySelector finds the button immediately. But even after calling initialize() again, no click even on the Tweet button fires.
What could be the issue here? My problem is that I don't even know where to look for and how to debug this.
After many more hours, I've finally figured it out. The problem was essentially the highly dynamic content of the new Twitter website. After submitting a tweet, the Tweet button gets indeed removed and added again. In needed to do a serious of changes:
Use a MutationObserver to keep track of any changes. Every time there's a change, call the initialize() function. To avoid too many calls, I do this in case of certain changes (unnecessary detail here)
Change the addSubmitNewTweetClickHandler() method so that the event listener first gets removed in order to avoid duplicate listeners (please note that I use objects hence the use of this compared to my original question)
addSubmitNewTweetClickHandler : function() {
let that = this;
let buttonSubmitTweet = document.querySelector("div[data-testid='tweetButtonInline']");
buttonSubmitTweet.removeEventListener('click', this.handleSubmitNewTweetClick );
this.handleSubmitNewTweetClick = this.handleSubmitNewTweetClick.bind(this)
buttonSubmitTweet.addEventListener('click', this.handleSubmitNewTweetClick );
},
This change required to create the reference function handleSubmitNewTweetClick
Overall, it's still not a perfect solution since I call initialize() many unnecessary time. However, I failed to reliably identify when the Tweet button was added to the document. When I used the MutationObserver none of the added nodes had the attribute data-testid which I need to identify the correct button. I have node idea why this attribute was not there. Maybe the attribute is added some times after added to button, but even with an additional MutationObserver looking for attribute changes I could detect this.
Anyway, it works now and it's only for a prototype.
I am using the google search API and I want that when you click on an image, this image will be copied to a different location.
I created a fiddle here: http://fiddle.jshell.net/wjewg062/
It works this way: The user types in a term in the input field and images will be displayed. When he/she clicks on one twice it will displayed in the image div.
I put the onClick event listener on to the searchresults div, hence the extra click in the beginning. However, I want it to be displayed on the first click.
Now, if I comment this out
var searchresults = document.getElementById('searchresults');
searchresults.addEventListener("click", function(event){
event.preventDefault();
imageing();
});
it doesn't work. The images will be links. I believe the reason for this is that the results are displayed in gs-image-box and not created yet. I tried calling the imaging function in different other functions like the searchImg or the OnLoad but nothing work.
I thought of using a check if element is clicked function described here Detect if I'm clicking an element within an element
but I think there must be an easier way.
I'm running out of ideas, can anyone give an idea or hint?
Thanks !
The images are dynamically created right? Check out this post Event binding on dynamically created elements?
In short, events are attached to elements upon pageload. so a newly created dynamic element such as the ones that google creates, aren't attached to the event. so google probably has a way to circumvent the whole "you need to load the page to attach events to elements" thing but it requires an extra click. Using the syntax found in the post should help you.
By the way. Using Jquery doesn't really show down your site because it's usually cached in the client's browser.
The info you need is already in your searchresults eventListener. The target of this event will be the image you click, even if you add the event on a div higher in the structure.
A javascript event will by default be dispatched from the top element (window) all the way through the element that received the click, then will go back to the top. Any element that is an ancestor of the element that was clicked will receive the event info, so you can listen on any ancestor, but the target remains the element that was actually clicked.
In your case, by simply passing the target to your imageing() function, you can apply the behaviors you want without any extra manipulations.
One problem you might face, is if user clicks on searchresult but not on an img element. Then you'll have a bug, so you should handle these cases.
Something like this:
var searchresults = document.getElementById('searchresults');
searchresults.addEventListener("click", function (event) {
console.log(event.target, this);
event.preventDefault();
if(event.target.tagName == 'IMG'){
imageing(event.target);
}
});
function imageing(targetImg) {
var imageresult = document.getElementsByClassName('gs-image-box');
var xu = document.getElementById('companylogo');
var imgsrc = targetImg.src;
xu.src = imgsrc;
}
http://fiddle.jshell.net/pwjLrfnt/3/
So i have a page in my site that has a with class name 'mainContent' that automatically updates with new data every like 1 minute using AJAX .ajax(). Content in this requires some JavaScript for some functionalities. The problem now is that JavaScript does not work on the new data loaded into the DOM without whole page refresh. I have searched and found using .on() to bind the data to the DOM should work, like so:
$(document).on('click', '.mainContent',function(){
expand();
});
where expand is a JS function.
However, it only works fully on the new data but not on the data that had been added in the previous AJAX call...
You're almost there, it's just your logic. This is how this jQuery function works:
You set a container. This is the element that will hold the AJAX-crated items that you want to bind. The more specific, the better. Otherwise, you'll wire an event for your whole page, which is bad
The event. What are you listening to?
Who will fire the handler. A selector to form the phrase: when these guys inside this big guy fire this event, run this code.
Let's suppose that your mainContent gets filled with hyperlinks (I'm not sure because your question lacks on details):
$('.mainContent').on('click', 'a', function () {
//do your magic
//$(this) is the clicked link
});
This way, our phrase is: when links inside .mainContent are clicked, run this.
UPDATE
Based on the comments, I think that your problem may be on the expand function.
Let's give a try:
$('.mainContent').on('click', 'a', function () {
$(this).simpleexpand();
});
Have you try to apply your binding on the callback of the ajax function that load your new datas ?
Something like that :
$.ajax({
url: url...//Classic ajax call
}).done(function ( data ) {
//Apply your 'on' here
});
Alright, so I'm making a Facebook-style chat. But that doesn't really matter.
See here:
http://jsfiddle.net/SkHme/7/
Nice and pretty, right? Well, there's a problem. Notice this line:
<div class="conversation EmperorCuzco" onclick="setActive('EmperorCuzco')">
See the onclick attribute? Well, it's not working. However, I have confirmed that the function itself DOES work. (if you run it just like that in the JavaScript, it runs like a dream) I have further confirmed that the function is not the problem by attempting to replace the onclick value with a simple alert('blah'), but that doesn't work either.
So, what's up? I'm guessing that something in my JavaScript is somehow disabling something, but I have absolutely no idea what it could be, nor how I could go about fixing it. I did some web searching, but couldn't find anything that helps.
What's going on?
Your setActive function is defined inside the scope of the $(document).ready handler. Move the function outside that function so that it is in the global scope.
Right now it looks like this:
$(document).ready(function()
{
// ...
function setActive(new_conversation)
{
// ...
}
});
Now change that to:
$(document).ready(function()
{
// ...
});
function setActive(new_conversation)
{
// ...
}
Really though, you should separate your content from your interactions and bind those event handlers in your script itself. Something like:
// Refers to the last clicked conversation *button*
// Initialize to empty jQuery object
var $active_conversation = $([]);
// Binding on #chat, targeting click events on .conversation_button children
$("#chat").on("click", ".conversation_button", function() {
// Hide currently active conversation
$active_conversation.hide();
$active_conversation.siblings('.conversation_button').removeClass("selected");
// Set as currently active conversation button
// Note: this refers to the clicked <div class="conversation_button">
var $this = $(this);
$active_conversation = $this.siblings('.conversation');
// Show this conversation
$active_conversation.show();
$this.removeClass("alert").addClass("selected");
});
Some advantages of this approach:
You don't need different classes for different conversations. By storing the actual conversation DOM element (as a jQuery object) in $active_conversation, the conversation can be identified without any extra processing.
You can add and remove whole list items with a conversation without assigning new event handlers. In the sample above, the event handler for all .conversation_button elements is defined at the level of #chat. For more about this binding mechanism, read up on .on (or .delegate for pre-1.7).
Here, have an updated fiddle! :-)
If all you say is really true (bad mistakes happen), the only thing that can make this is that an other event handler which takes your event before uses stopPropagation() or return false;
A quick check that can do is replace onclick with onmousedown or onmouseup, and see if you alert become visible.
I want to attach a function to a jQuery element that fires whenever the element is added to the page.
I've tried the following, but it didn't work:
var el = jQuery('<h1>HI HI HI</H1>');
el.one('load', function(e) {
window.alert('loaded');
});
jQuery('body').append(el);
What I really want to do is to guarantee that another jQuery function that is expecting some #id to be at the page don't fail, so I want to call that function whenever my element is loaded in the page.
To clarify, I am passing the el element to another library (in this case it's a movie player but it could be anything else) and I want to know when the el element is being added to the page, whether its my movie player code that it is adding the element or anything else.
I want to attach a function to a
jQuery element that fires whenever the
element is added to the page.
You want the livequery plugin, which does just this. The recent live function is similar, except it won't call you when the element is added. We use it all the time-- works great.
You'll use $('h1').livequery(function() {alert('just added');});
I do not know that there is this type of event, what comes to mind is creating the event "el-load" based on this tutorial, and then extend "append" to know if the item has this event make the call to it.
Use LiveQuery (jQuery plugin), and attach a load event to ur dom element (h1), in this case.
try overwriting the append method so you can add your own event?
jQuery.extend(jQuery.fn, {
_append: jQuery.fn.append,
append: function(j) {
this._append(j);
j.trigger('append');
}
});
var el = jQuery('<h1>HI HI HI</H1>');
el.one('append', function(e) {
window.alert('loaded');
});
jQuery('body').append(el);
If the tag is being created via ajax, you can use a related node to subscribe to the ajaxSuccess event.
http://docs.jquery.com/Ajax/ajaxSuccess
$('#somenode').ajaxSuccess(function(){
if ($('h1').length > 0) { ... }
});
If it's just being added to the DOM by a local script, I'm not sure it's possible to observe it's creation, with the exception of using a timer to poll for it.
Depending upon the browsers you need to support there are DOMNodeInserted and DOMNodeInsertedIntoDocument events. Can't vouch for how well they work myself but theoretically you could bind to these events and then either check the new node and the possible subtree that was inserted, or just check the entire document again with a $(selector) of your choosing to detect the node you're waiting to see added.
Try these:
var el = jQuery('<h1>HI HI HI</H1>');
jQuery('body').append(el);
setTimeout(function(){
window.alert('loaded');
},1000);//delay execution about 1 second
or to be safe this one:
var el = jQuery('<h1>HI HI HI</H1>');
jQuery('body').append(el);
window.checker = setInterval(function(){
if($('someselector').length>0){ //check if loaded
window.alert('loaded');
clearInterval(window.checker);
}
},200);
basically, this will loop every 200ms until the selector gets result, and terminates the loop when the result is available