Storing an iframe's elements (the ones in the page that it is has loaded -- like <table>) in a variable - variables

How can I store the elements of an iframe into a variable?
This is something I want to happen within the parent.
I know the elements should have an id to implement this repeatedly proposed solution that does not work:
var foo = document.getElementbyId([IFRAME ID]).contentWindow.document.getElementbyID([ELEMENT ID]);
nor does this code work:
var foo = document.getElementbyId([IFRAME ID]).contentDocument.getElementbyID([ELEMENT ID]);
Saving each section into individual variables makes no difference.
i.e.
var x = document.getElementbyId([IFRAME ID]);
var y = (x.contentDocument || x.contentWindow.document);
var foo = y.getElementbyID([ELEMENT ID]);
What works? Please demonstrate a solution in jfiddle.
Thanks.
UPDATE:
I tried running this function with the iframe id passed into it (on my site there is a jukebox page which is loaded into the main iFrame and it has an iFrame itself -- which has an id called 'albumcover'):
function showIframeContent(id) {
var iframe = document.getElementById(id);
try {
var doc = (iframe.contentDocument)? iframe.contentDocument: iframe.contentWindow.document;
alert("The jukebox page's iframe URL is" +
doc.getElementById('albumcover').contentWindow.location.href);
}
catch(e) {
alert(e.message);
}
return false;
}
I then received this message:
[ Permission denied to access property "href" ]
-- 100% of my site, all referenced data...everything, is within a public_html folder...same domain, same hosting service area...the works.
What in the world is going on?
Thanks again.

Related

Panning disabled when remove leaflet map from dom -vuejs

I have a leaflet map in my vuejs app. I need to refresh my map when users update their search terms.
I notice that my code is not replacing the map. Instead, my code adds more divs to the existing map. This is problematic because it interferes with user panning and also overloads the page with unwanted data.
I have tried deleting the existing map in several ways...
My current approach is this...
var container = L.DomUtil.get("leafletMapId");
if (container != null) {
while (container.firstChild)
container.removeChild(containerfirstChild);
}
container._leaflet_id = null;
} else {
console.log("container was null");
}
var myMap = L.map("leafletMapId", {
layers: [streetTilesLayer],
scrollWheelZoom: true
}).setView(this.center, this.zoom);
This appears to effectively empty the map div. However, it leads to an error when I click and attempt to pan the map:
leaflet-src.js?e11e:2558 Uncaught TypeError: Cannot read property
'offsetWidth' of null
at getSizedParentNode (leaflet-src.js?e11e:2558)
at NewClass._onDown (leaflet-src.js?e11e:5902)
at HTMLDivElement.handler (leaflet-src.js?e11e:2679)
I have also tried this...
var container = L.DomUtil.get("leafletMapId");
if (container != null) {
container.innerHTML = "";
container._leaflet_id = null;
} else {
console.log("container was null");
}
var myMap = L.map("leafletMapId", {
layers: [streetTilesLayer],
scrollWheelZoom: true
}).setView(this.center, this.zoom);
This causes the same error.
This seems to completely replace the leaflet map with no errors...
$("#leafletMapId").remove();
var g = document.createElement("div");
g.setAttribute("id", "leafletMapId");
document.getElementById("main-div").appendChild(g);
var myMap = L.map("leafletMapId", {
layers: [streetTilesLayer],
scrollWheelZoom: true
}).setView(this.center, this.zoom);
where this the div with ID "leafletMapId" is a child of the div with ID "main-div".
I've been struggling with this problem for a day and the answer given by GNG was the only one that solved it for me. I had to combine his answer with another one I found, so I could reload my map with a different set of markers each time and still have panning controls. My final code is this.
$("#map").remove();
var g = document.createElement("div");
g.setAttribute("id", "map");
document.getElementById("main-div").appendChild(g);
document.getElementById('map').innerHTML = `<div id="map" class="map map-home" style="height: 300px; margin-top: 50px"></div>`;
var osmUrl = 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
osmAttribution = 'Attribution',
osmLayer = new L.TileLayer(osmUrl, {maxZoom: 18, attribution: osmAttribution});
var map = L.map('map').setView([latitud, longitud], 15).addLayer(osmLayer);
L.marker([latitud, longitud])
.addTo(map)
.bindPopup("mensaje")
.openPopup();
I post this not as an answer to the original question, but to further comment on the answer given by GNG.

popstate keeps using last state

I got a simple code when users are filtering brands, categories etc..
Everything works as it should.
BUT when you for example want to go back twice, the first popstate returns the last page perfectly. But when you go back again, it still returns the last page, so it loads it again. And not the page before that one.
A piece of my code:
The pushstate
var params = $('fme_layered_params').value.parseQuery();
// Object { cat="274", dir="desc"}
if (!params['dir'])
{
$('fme_layered_params').value += '&dir=' + 'desc';
}
if(window.location.href.indexOf('?') > -1){
var u = window.location.href.split("?");
var url = u[0]+"?"+$('fme_layered_params').value;
}else
var url = window.location.href+"?"+$('fme_layered_params').value;
window.history.pushState($('fme_layered_params').value, 'link', url);
The popstate
window.onpopstate = function(e) {
if(e.state!==null){
var state = e.state;
state = state.split("&");
// ["cat=272", "dir=desc"]
jQuery.each(state, function(i,v){
var info = v.split('=');
fme_layered_add_params(info[0], info[1], 1);
});
fme_layered_make_request();
}
};
What am i doing wrong?
Thanks in advance!
I solved this problem.
Every time i went back it also pushed the current page. So i went back and the same page was pushed in history resulting in endless reloading of that certain page.
TL;DR?
check, double check, triple check how your code 'walks' through the lines.

Phantomjs dump dom object

I'm new to Phantomjs. For debugging on a remote server, I often want to dump a DOM object to look at the structure (similar to Data::Dumper in Perl). This currently is for scraping a couple of sites.
I've thought JSON.stringify may help with this, but it still displays an object name like "[object HTMLDocument]"
Edit: I have also looked at JavaScript: how to serialize a DOM element as a string to be used later? , but I can't seem to inject jquery in phantomjs (still looking for a solution to that, and would prefer no depencencies), and the other answer doesn't seem to work. As I assume it would be a common case for Phantom to analyse the DOM, I thought it would be common for phantom users to have a solution to this.
var page = require('webpage').create();
var system = require('system');
page.onConsoleMessage = function(msg) {
console.log( msg );
}
page.open('http://www.test.com', function(status) {
if(status !== "success") {
console.log( status );
} else {
page.evaluate(function() {
var headline = document.querySelectorAll('div');
console.log( JSON.stringify( headline ) ); // HERE???
});
}
phantom.exit();
});
Is there any way to do this, or am I approaching this wrong ?
in page.evaluate(), you can use XMLSerializer.serializeToString() to convert whatever DOM node you want to string.
page.evaluate(function() {
var s = new XMLSerializer();
return s.serializeToString(document.getElementById('div'));
});
I haven't tried it with "querySelectorAll", since it may return array instead of standalone DOM node, but it definitely works for DOM nodes.
MDN Link

Google Script - Adding dynamic parameters to href from handler

I have a Google Script published as a web app which uses UI service to display an interface with several listboxes. I can get at the values selected thru server handlers.
My problem is that I need to add these values to a url in a anchor defined in my doGet routine. (I am calling a JotForm url, and need the dynamic parameters to pre-populate the form)
I can't see how to modify the anchor from the handler function, or any other way to invoke the url I build in code.
When you want to modify any widget in a Ui created with UiApp, each widget must have an ID that you can use to getElementById() and manipulate the way you want just as if you were in the doGet function.
Here is a simple example to illustrate : (online here)
function doGet(){
var app = UiApp.createApplication().setTitle('test');
var serieNames = [' serie A',' serie B',' serie C'];
var panel = app.createVerticalPanel().setStyleAttribute('padding','30px');
var namesHandler = app.createServerHandler('showPilots').addCallbackElement(panel);
for(var n in serieNames){
var serieSelect = app.createRadioButton('pilotSelect',serieNames[n]).setId('s'+n).addClickHandler(namesHandler)
panel.add(serieSelect);
}
app.add(panel);
return app;
}
function showPilots(e){
Logger.log(JSON.stringify(e));// you can see the source parameter in e that returns the widgets ID of the button you clicked to call the handler
var app = UiApp.getActiveApplication();
var serie = e.parameter.source; // get the ID
app.add(app.createLabel('you clicked '+e.parameter.source));// then get this widget by its ID and modify it
app.getElementById(serie).setText('Clicked');// modify it
return app;// update Ui
}
EDIT : here is a version that manipulates anchors, it is perfectly possible to change the url from a handler.
test here
code :
function doGet(){
var app = UiApp.createApplication().setTitle('test');
var links = ['link 1',' link 2',' link 3'];
var linkshref = ['http://www.google.com','http://www.packtpub.com/google-apps-script-for-beginners/book','http://stackoverflow.com/questions/tagged/google-apps-script'];
var panel = app.createVerticalPanel().setStyleAttribute('padding','30px');
var namesHandler = app.createServerHandler('changeUrl').addCallbackElement(panel);
for(var n in links){
var linkWidget = app.createAnchor(links[n], linkshref[n]).setId('s'+n);
panel.add(linkWidget);
}
var btn = app.createButton('change links',namesHandler);
app.add(panel.add(btn));
return app;
}
function changeUrl(e){
Logger.log(JSON.stringify(e));// you can see the source parameter in e that returns the widgets ID of the button you clicked to call the handler
var app = UiApp.getActiveApplication();
var links = ['New link 1','New link 2','new link 3'];
var linkshref = ['http://www.microsoft.com','http://www.w3schools.com/js/','https://sites.google.com/site/appsscriptexperiments/'];
for(var n in links){
app.getElementById('s'+n).setHref(linkshref[n]).setHTML(links[n]);
}
return app;// update Ui
}

Yii renderpartial (proccessoutput = true) Avoid Duplicate js request

Im creating a site who works with ajaxRequest, when I click a link, it will load using ajaxRequest. When I load for example user/login UserController actionLogin, I renderPartial the view with processOUtput to true so the js needed inside that view will be generated, however if I have clientScriptRegister inside that view with events, how can I avoid to generate the scriptRegistered twice or multiple depending on the ajaxRequest? I have tried Yii::app()->clientScript->isSCriptRegistered('scriptId') to check if the script is already registered but it seems that if you used ajaxRequest, the result is always false because it will only be true after the render is finished.
Controller code
if (Yii::app()->request->isAjaxRequest)
{
$this->renderPartial('view',array('model'=>$model),false,true);
}
View Code
if (!Yii::app()->clientScript->isScriptregistered("view-script"))
Yii::app()->clientScript->registerScript("view-script","
$('.link').live('click',function(){
alert('test');
})
");
If I request for the controller for the first time, it works perfectly (alert 1 time) but if I request again for that same controller without refreshing my page and just using ajaxRequest, the alert will output twice if you click it (because it keeps on generating eventhough you already registered it once)
This is the same if you have CActiveForm inside the view with jquery functionality.. the corescript yiiactiveform will be called everytime you renderPartial.
To avoid including core scripts twice
If your scripts have already been included through an earlier request, use this to avoid including them again:
// For jQuery core, Yii switches between the human-readable and minified
// versions based on DEBUG status; so make sure to catch both of them
Yii::app()->clientScript->scriptMap['jquery.js'] = false;
Yii::app()->clientScript->scriptMap['jquery.min.js'] = false;
If you have views that are being rendered both independently and as HTML fragments to be included with AJAX, you can wrap this inside if (Yii::app()->request->isAjaxRequest) to cover all bases.
To avoid including jQuery scripts twice (JS solution)
There's also the possibility of preventing scripts from being included twice on the client side. This is not directly supported and slightly more cumbersome, but in practice it works fine and it does not require you to know on the server side what's going on at the client side (i.e. which scripts have been already included).
The idea is to get the HTML from the server and simply strip out the <script> tags with regular expression replace. The important point is you can detect if jQuery core scripts and plugins have already been loaded (because they create $ or properties on it) and do this conditionally:
function stripExistingScripts(html) {
var map = {
"jquery.js": "$",
"jquery.min.js": "$",
"jquery-ui.min.js": "$.ui",
"jquery.yiiactiveform.js": "$.fn.yiiactiveform",
"jquery.yiigridview.js": "$.fn.yiiGridView",
"jquery.ba-bbq.js": "$.bbq"
};
for (var scriptName in map) {
var target = map[scriptName];
if (isDefined(target)) {
var regexp = new RegExp('<script.*src=".*' +
scriptName.replace('.', '\\.') +
'".*</script>', 'i');
html = html.replace(regexp, '');
}
}
return html;
}
There's a map of filenames and objects that will have been defined if the corresponding script has already been included; pass your incoming HTML through this function and it will check for and remove <script> tags that correspond to previously loaded scripts.
The helper function isDefined is this:
function isDefined(path) {
var target = window;
var parts = path.split('.');
while(parts.length) {
var branch = parts.shift();
if (typeof target[branch] === 'undefined') {
return false;
}
target = target[branch];
}
return true;
}
To avoid attaching event handlers twice
You can simply use a Javascript object to remember if you have already attached the handler; if yes, do not attach it again. For example (view code):
Yii::app()->clientScript->registerScript("view-script","
window.myCustomState = window.myCustomState || {}; // initialize if not exists
if (!window.myCustomState.liveClickHandlerAttached) {
window.myCustomState.liveClickHandlerAttached = true;
$('.link').live('click',function(){
alert('test');
})
}
");
The cleanest way is to override beforeAction(), to avoid any duplicated core script:
class Controller extends CController {
protected function beforeAction($action) {
if( Yii::app()->request->isAjaxRequest ) {
Yii::app()->clientScript->scriptMap['jquery.js'] = false;
Yii::app()->clientScript->scriptMap['jquery-2.0.0.js'] = false;
Yii::app()->clientScript->scriptMap['anything.js'] = false;
}
return parent::beforeAction($action);
}
}
Note that you have to put the exact js file name, without the path.
To avoid including script files twice, try this extension: http://www.yiiframework.com/extension/nlsclientscript/
To avoid attaching event handlers twice, see Jons answer: https://stackoverflow.com/a/10188538/729324