How to determine if content in popup window is loaded - javascript

I need to set some contextData for a popup window from its parent. I try this:
var some contextData = {};
$(function() {
$('#clickme').click(function() {
var w = window.open('http://jsfiddle.net');
w.contextData = contextData
//w.context data is null in the popup after the page loads - seems to get overwritten/deleted
});
});
It doesn't work, so my next thought, wait until content is loaded
var some contextData = {};
$(function() {
$('#clickme').click(function() {
var w = window.open('http://jsfiddle.net');
w.onload = function() {
//Never Fires
w.contextData = contextData;
}
});
});
See this fiddle. My onload method never fires.
This works:
var some contextData = {};
$(function() {
$('#clickme').click(function() {
var w = window.open('http://jsfiddle.net');
setTimeout(function(){
if(w.someVariableSetByThePageBeingLoaded) {
w.contextData = contextData;
}
else{
setTimeout(arguments.callee, 1);
}
}, 1);
});
});
But has obvious elegance problems (but is the current work around).
I know you can go the other way (have the popup call back to a method on the opener/parent, but this forces me to maintain some way of looking up context (and I have to pass the key to the context to the popup in the query string). The current method lets me capture the context in a closure, making my popup a much more reusable piece of code.
I am not trying to do this cross domain - both the parent and popup are in the same domain, although the parent is an iframe (hard to test with jsfiddle).
Suggestions?

If you are doing this with an iframe try it this way
HTML
<button id="clickme">Click Me</button>
<iframe id="framer"></iframe>
​
Javascript
$(function() {
$('#clickme').click(function() {
$("#framer").attr("src","http://jsfiddle.net");
$("#framer")[0].onload = function(){
alert('loaded');
};
});
});​
I updated your jsfiddle http://jsfiddle.net/HNvn3/2/
EDIT
Since the above is completely wrong this might point you in the right direction but it needs to be tried in the real environment to see if it works.
The global variable frames should be set and if you
window.open("http://jsfiddle.net","child_window");
frames["child_window"] might refer to the other window
I got javascript access errors when trying it in jsfiddle - so this might be the right track
EDIT2
Trying out on my local dev box I was able to make this work
var w = window.open("http://localhost");
w.window.onload = function(){
alert("here");
};
the alert() happened in the parent window

Related

Jquery .change() event fires only once

So I'm fairly novice with jquery and js, so I apologise if this is a stupid error but after researching I can't figure it out.
So I have a list of data loaded initially in a template, one part of which is a dropdown box that lets you filter the data. My issue is that the filtering only works once? As in, the .change function inside $(document).ready() only fires the once.
There are two ways to reload the data, either click the logo and reload it all, or use the search bar. Doing either of these at any time also means the .change function never fires again. Not until you refresh the page.
var list_template, article_template, modal_template;
var current_article = list.heroes[0];
function showTemplate(template, data)
{
var html = template(data);
$("#content").html(html);
}
$(document).ready(function()
{
var source = $("#list-template").html();
list_template = Handlebars.compile(source);
source = $("#article-template").html();
article_template = Handlebars.compile(source);
source = $("#modal-template").html();
modal_template = Handlebars.compile(source);
showTemplate(list_template,list);
$(".articleButton").click(function()
{
var index = $(this).data("id");
current_article = list.heroes[index];
showTemplate(article_template,current_article);
$('.poseThumb').click(displayModal);
});
$("#classFilter").change(function()
{
console.log("WOW!");
var classToFilter = this.value;
var filteredData =
{
heroes: list.heroes.filter(function(d)
{
if (d.heroClass.search(classToFilter) > -1)
{
return true;
}
return false;
})
};
console.log(filteredData);
showTemplate(list_template,filteredData);
$(".articleButton").click(function()
{
var index = $(this).data("id");
current_article = filteredData.heroes[index];
showTemplate(article_template,current_article);
$('.poseThumb').click(displayModal);
});
});
$("#searchbox").keypress(function (e)
{
if(e.which == 13)
{
var rawSearchText = $('#searchbox').val();
var search_text = rawSearchText.toLowerCase();
var filteredData =
{
heroes: list.heroes.filter(function(d)
{
if (d.name.search(search_text) > -1)
{
return true;
}
return false;
})
};
console.log(filteredData);
showTemplate(list_template,filteredData);
$(".articleButton").click(function()
{
var index = $(this).data("id");
current_article = filteredData.heroes[index];
showTemplate(article_template,current_article);
$('.poseThumb').click(displayModal);
});
}
});
$("#logo").click(function()
{
showTemplate(list_template,list);
$(".articleButton").click(function()
{
var index = $(this).data("id");
current_article = list.heroes[index];
showTemplate(article_template,current_article);
$('.poseThumb').click(displayModal);
});
});
//$("#logo").click();
});
function displayModal(event)
{
var imageNumber = $(this).data("id");
console.log(imageNumber);
var html = modal_template(current_article.article[0].vicPose[imageNumber]);
$('#modal-container').html(html);
$("#imageModal").modal('show');
}
I should note two things: first, that the search bar works perfectly, and the anonymous function inside both of them is nearly identical, and like I said, the filtering works perfectly if you try it after the initial load. The second is that the same problem occurs replacing .change(anonymous function) with .on("change",anonymous function)
Any help or advice would be greatly appreciated. Thanks.
I agree with Fernando Urban's answer, but it doesn't actually explain what's going on.
You've created a handler attached to an HTML element (id="classFilter") which causes part of the HTML to be rewritten. I suspect that the handler overwrites the HTML which contains the element with the handler on it. So after this the user is clicking on a new HTML element, which looks like the old one but doesn't have a handler.
There are two ways round this. You could add code inside the handler which adds the handler to the new element which has just been created. In this case, that would mean making the handler a named function which refers to itself. Or (the easier way) you could do what Fernando did. If you do this, the event handler is attached to the body, but it only responds to clicks on the #classFilter element inside the body. In other words, when the user clicks anywhere on the body, jQuery checks whether the click happened on a body #classFilter element. This way, it doesn't matter whether the #classFilter existed when the handler was set. See "Direct and delegated events" in jQuery docs for .on method.
Try to use some reference like 'body' in the event listeners inside your DOM like:
$('body').on('click','.articleButton', function() {
//Do your stuff...
})
$('body').on('click','#classFilter', function() {
//Do your stuff...
})
$('body').on('keypress','#searchbox', function() {
//Do your stuff...
})
$('body').on('click','#logo', function() {
//Do your stuff...
})
This will work that you can fire it more than once.

Handling State changes jQuery and History.js

Ok, so I need some insight into working with History.js and jQuery.
I have it set up and working (just not quite as you'd expect).
What I have is as follows:
$(function() {
var History = window.History;
if ( !History.enabled ) {
return false;
}
// Capture all the links to push their url to the history stack and trigger the StateChange Event
$('.ajax-link').click(function(e) {
e.preventDefault();
var url = this.href; //Tells us which page to load
var id = $(this).data('passid'); //Pass ID -- the ID in which to save in our state object
e.preventDefault();
console.log('url: '+url+' id:'+id);
History.pushState({ 'passid' : id }, $(this).text(), url);
});
History.Adapter.bind(window, 'statechange', function() {
console.log('state changed');
var State = History.getState(),
id = State.data.editid; //the ID passed, if available
$.get(State.url,
{ id: State.data.passid },
function(response) {
$('#subContent').fadeOut(200, function(){
var newContent = $(response).find('#subContent').html();
$('#subContent').html(newContent);
var scripts = $('script');
scripts.each(function(i) {
jQuery.globalEval($(this).text());
});
$('#subContent').fadeIn(200);
});
});
});
}); //end dom ready
It works as you'd expect as far as changing the url, passing the ID, changing the content. My question is this:
If I press back/forward on my browser a couple times the subContent section will basically fadeIn/fadeOut multiple times.
Any insight is appreciated. Thanks
===================================================
Edit: The problem was in my calling all of my <script> and Eval them on each statechange. By adding a class="no-reload" to the history controlling script tag I was able to do:
var scripts = $('script').not('.no-reload');
This got rid of the problem and it now works as intended. Figure I will leave this here in case anyone else runs into the same issue as I did.
The problem was in my calling of all of my <script> and Eval them on each statechange. By adding a class="no-reload" to the history controlling script tag I was able to do:
var scripts = $('script').not('.no-reload');
This got rid of the problem and it now works as intended. Figure I will leave this here in case anyone else runs into the same issue as I did.

How to pass data from content-script to page-level?

I'm injecting all my js code to front page, but it needs pictures for ui and stuff, that can be imported only with the help of chrome.extension.getUrl and can be called only from content-script, so I've found tons of advices how to pass data to content page, and nothing of about how pass data back, is it possible at all?
My code now looks like this:
my js code, that will be injected with other code:
var Content = {};
$(document).contentReady = function(content) {
Content = content;
$(document).ready(function () {/*cool stuff here, that require content*/});
}
var event = new CustomEvent('LoadContent');
window.dispatchEvent(event);
content-script:
document.querySelector('head').appendChild(jsCode);
window.addEventListener("LoadContent", function(evt) {
var content =
{
data: "url(" + chrome.extension.getURL('content.smth') + ")"
};
document.contentReady(content);
}, false);
And, obviously, I get document.contentReady is not a function
But declaring function in document was the only(!) advice of about how to pass data back from content-script after about 2 hours of googling.
Nothing stops you from making the CustomEvent-based communication bi-directional, and it can pass data with detail property:
// Page script
window.addEventListener('RecieveContent', function(evt) {
// do something cool with evt.detail
});
var event = new CustomEvent('LoadContent');
window.dispatchEvent(event);
// Content script
window.addEventListener('LoadContent', function(evt) {
content = /* ... */
var event = new CustomEvent('RecieveContent', {detail: content});
window.dispatchEvent(event);
});
A more in-depth answer can be found here.
However, you should ask yourself whether you even need the page-level script to query for data, since you fully control when it's injected. You can use uni-directional approach after you make sure the code has executed:
// Page script
window.addEventListener('RecieveContent', function(evt) {
// do something cool with evt.detail
});
// Content script
jsCode.onload = function() {
// This fires after the page script finishes executing
content = /* ... */
var event = new CustomEvent('RecieveContent', {detail: content});
window.dispatchEvent(event);
}
document.querySelector('head').appendChild(jsCode);
You can pass JS data to the page by creating a new script tag. For example:
function injectScript(code) {
var body = document.getElementsByTagName('body')[0];
var s = document.createElement('script');
s.innerHTML = code;
body.appendChild(s);
}
injectScript('var foo = 2;');
So for your particular example, you should be able to do:
injectScript('document.contentReady({data: url(' + blahblah + '})');
Not pretty (what is when you're working with overwriting content scripts?) but it works.
Content Scripts do not share window object with normal scripts on the page. Both of them work on different context.
In your case, you are registering an event listener on window and listening for the event on other context (window). Hence, your event listener will never be called.
However, there is one alternative approach I can see to communicate between content script and normal script is by using MutationObserver.
Idea
Define a node with some Id under which you will create subnodes corresponding to an event.
Register Mustation Observer in your script.
From content script, add the nodes with data as data-* api.
Implementation Example
Content Script
var submitEvent = function(category, action, label) {
var eventObserverPlaceholder = document.getElementById('events-observer-placeholder'),
$eventEl = $('<span></span>').attr({
'data-category': category,
'data-action': action,
'data-label': label
});
eventObserverPlaceholder.appendChild($eventEl.get(0));
};
Normal Script for registering Mutation Observer:
RQ.Methods.addObserverForEvents = function(targetNode) {
var observer = new MutationObserver(RQ.Methods.handleMutationList);
// Notify me when a new child is added
var observerConfig = {
attributes: false,
childList: true,
characterData: false
};
observer.observe(targetNode, observerConfig);
return observer;
};
RQ.mutationObserver = RQ.Methods.addObserverForEvents(document.getElementById('events-observer-placeholder'));
Links
https://davidwalsh.name/mutationobserver-api
https://developer.mozilla.org/en/docs/Web/API/MutationObserver
Working Example:
I have used the same approach in Requestly Chrome Extension for submitting events to Google Analytics.
Content Script: https://github.com/requestly/chrome-extension/blob/master/src/Shared/utils.js#L26
Normal Script: https://github.com/requestly/web/blob/gh-pages/js/scripts/tracker.js#L35

automatic clicks on links and doing something with every page's DOM

i have some links in a web page ,what i want to do :
Trigger click event on every link
When the page of every link is loaded , do something with page's DOM(fillProducts here)
What i have tried :
function start(){
$('.category a').each(function(i){
$.when($(this).trigger('click')).done(function() {
fillProducts() ;
});
})
}
Thanks
What you want to do is much more complicated than you seem to be giving it credit for. If you could scrape webpages, including AJAX content, in 7 lines of js in the console of a web browser you'd put Google out of business.
I'm guessing at what you want a bit, but I think you want to look at using a headless browser, e.g. PhantomJs. You'll then be able to scrape the target pages and write the results to a JSON file (other formats exist) and use that to fillProducts - whatever that does.
Also, are you stealing data from someone else's website? Cause that isn't cool.
Here's a solution that may work for you if they are sending their ajax requests using jQuery. If they aren't you're going to need to get devilishly hacky to accomplish what you're asking (eg overriding the XMLHttpRequest object and creating a global observer queue for ajax requests). As you haven't specified how they're sending the ajax request I hope this approach works for you.
$.ajaxSetup({
complete: function(jQXHR) {
if(interested)
//do your work
}
});
The code below will click a link, wait for the ajax request to be sent and be completed, run you fillProducts function and then click the next link. Adapting it to run all the clicks wouldn't be difficult
function start(){
var links = $('.category a');
var i = 0;
var done = function() {
$.ajaxSetup({
complete: $.noop//remove your handler
});
}
var clickNext = function() {
$(links.get(i++)).click();//click current link then increment i
}
$.ajaxSetup({
complete: function(jQXHR) {
if(i < links.length) {
fillProducts();
clickNext();
} else {
done();
}
}
});
clickNext();
}
If this doesn't work for you try hooking into the other jqXHR events before hacking up the site too much.
Edit here's a more reliable method in case they override the complete setting
(function() {
var $ajax = $.ajax;
var $observer = $({});
//observer pattern from addyosmani.com/resources/essentialjsdesignpatterns/book/#observerpatternjquery
var obs = window.ajaxObserver = {
subscribe: function() {
$observer.on.apply($observer, arguments);
},
unsubscribe: function() {
$observer.off.apply($observer, arguments);
},
once: function() {
$observer.one.apply($observer, arguments);
},
publish: function() {
$observer.trigger.apply($observer, arguments);
}
};
$.ajax = function() {
var $promise = $ajax.apply(null, arguments);
obs.publish("start", $promise);
return $promise;
};
})();
Now you can hook into $.ajax calls via
ajaxObserver.on("start", function($xhr) {//whenever a $.ajax call is started
$xhr.done(function(data) {
//do stuff
})
});
So you can adapt the other snippet like
function start(){
var links = $('.category a');
var i = 0;
var clickNextLink = function() {
ajaxObserver.one("start", function($xhr) {
$xhr.done(function(data) {
if(i < links.length) {
fillProducts();
clickNextLink();
} else {
done();
}
});
})
$(links.get(i++)).click();//click current link then increment i
}
clickNextLink();
}
try this:
function start(){
$('.category a').each(function(i){
$(this).click();
fillProducts() ;
})
}
I get ya now. This is like say:
when facebook loads, I want to remove the adverts by targeting specific class, and then alter the view that i actually see.
https://addons.mozilla.org/en-US/firefox/addon/greasemonkey/
Is a plugin for firefox, this will allow you to create a javascript file, will then allow you to target a specific element or elements within the html rendered content.
IN order to catch the ajax request traffic, you just need to catcher that within your console.
I can not give you a tutorial on greasemonkey, but you can get the greasemonkey script for facebook, and use that as a guide.
http://mashable.com/2008/12/25/facebook-greasemonkey-scripts/
hope this is it

Access to every iframe child

I'm working with a client proyect which is working with lot's of iframes inside others.
The problem is that I want to add an addEventListener in all iframes automatically from the absolute parent document, an example schema would be this:
Document: iframe1, iframe2
Iframe1: iframe1.1, iframe1.2
Iframe1.1: iframe.1.1.1, iframe 1.1.2
I want to simulate something like this:
iframes[i].contentWindow.document.getElementsByTagName('iframe')[0].contentWindow.document.getElementsByTagName('iframe')[i]
I wrote this code, but I have an "undefined" error:
window.onload=function(){
eventoIframe(document,null,1);
}
var eventoIframe=function(target,prev_target,lvl){
console.log('Ejecuto funcion por '+lvl+' vez');
var iframes=target.getElementsByTagName('iframe');
if(lvl>2){
var iframes=prev_target.target.getElementsByTagName('iframe');
}
if(iframes.length>0){
for(var i=0;i<=iframes.length-1;i++){
var iframe=iframes[i].contentWindow;
iframe.addEventListener("click",function(event){
console.log('click on:', event.target)
},false);
eventoIframe(iframe.document,target,lvl+1);
}
}
}
Okay, I found the mistake, I don't know why I wanted to know the prev target hehe
The solution is
window.onload=function(){
eventoIframe(document);
}
var eventoIframe=function(target){
var iframes=target.getElementsByTagName('iframe');
if(iframes.length>0){
for(var i=0;i<=iframes.length-1;i++){
var iframe=iframes[i].contentWindow;
iframe.addEventListener("click",function(event){
//code to be execute on each iframe
},false);
eventoIframe(iframe.document);
}
}
}

Categories

Resources