My iframe:
<iframe frameborder="0" width="100%" height="100%" src="https://csfe-preprod.bankid.no/CentralServerFEJS/a?cid=KF3c3j2c6XcWLdRN" title="BankID"></iframe>
Inside it is the element i want to interact with.
The problem is that with every tutorial/post on google/a couple on stackoverflow i've found, i just get the "element not found by selector" error every time.
Some i've tried: (i've tried multiple but i've erased them)
var driver = browser.driver;
var loc = by.css('iframe[title="BankID"]');
var el = driver.findElement(loc);
browser.switchTo().frame(el);
browser.switchTo().frame(element(by.xpath('//*[#id="bankid-container"]/iframe')).getWebElement());
I can't seem to understand why i can't interact with the elements. In the browser's console i can easly indentify them with a simple jquery css selector $('iframe[title="BankID"]') and multiple others, they work, i know they do but i can't seem to be able to interact with them.
I've tried I think all the possible ways of changing the frame, i'm really at my edge here...
Related
At the moment I can retrieve the contents of the first iframe on a web page that is not mine with $('iframe').contents()
From this I can get the body tag by doing $('iframe').contents().find('body'). The results are what is expected
However, I an trying to get the body tag of the second iframe. By doing `$('iframe').eq(1) I can get the second iframe, but $('iframe').eq(1).contents() gets nothing.
How can I get the contents of the second iframe?
A lot of the Fiddles and such may not be a good test place. There are complications with CORS. Here is an example that works:
https://jsfiddle.net/Twisty/037yueqj/9/
HTML
<iframe id="frame-1"></iframe>
<iframe id="frame-2"></iframe>
JavaScript
$(function() {
var f1 = $("#frame-1"),
f2 = $("#frame-2"),
frames = $("iframe");
f1.contents().find("body").html("<strong>Frame 1</strong>");
f2.contents().find("body").html("<strong>Frame 2</strong>");
console.log(frames.eq(0).contents().find("body").text());
console.log(frames.eq(1).contents().find("body").text());
});
If you give them unique IDs or classes, you can just call on the proper selector. You can call on a wider selector and use .eq() or iterate with .each().
Had to use --disable-web-security in chromium to access contents in an iframe from another url
Take a look at this simple HTML:
<div id="wrap1">
<iframe id="iframe1"></iframe>
</div>
<div id="warp2">
<iframe id="iframe2"></iframe>
</div>
Let's say I wanted to move the wraps so that the #wrap2 would be before the #wrap1. The iframes are polluted by JavaScript. I am aware of jQuery's .insertAfter() and .insertBefore(). However, when I use those, the iFrame loses all of its HTML, and JavaScript variables and events.
Lets say the following was the iFrame's HTML:
<html>
<head>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript">
// The variable below would change on click
// This represents changes on variables after the code is loaded
// These changes should remain after the iFrame is moved
variableThatChanges = false;
$(function(){
$("body").click(function(){
variableThatChanges = true;
});
});
</script>
</head>
<body>
<div id='anything'>Illustrative Example</div>
</body>
</html>
In the above code, the variable variableThatChanges would...change if the user clicked on the body. This variable, and the click event, should remain after the iFrame is moved (along with any other variables/events that have been started)
My question is the following: with JavaScript (with or without jQuery), how can I move the wrap nodes in the DOM (and their iframe childs) so that the iFrame's window stays the same, and the iFrame's events/variables/etc stay the same?
It isn't possible to move an iframe from one place in the dom to another without it reloading.
Here is an example to show that even using native JavaScript the iFrames still reload:
http://jsfiddle.net/pZ23B/
var wrap1 = document.getElementById('wrap1');
var wrap2 = document.getElementById('wrap2');
setTimeout(function(){
document.getElementsByTagName('body')[0].appendChild(wrap1);
},10000);
This answer is related to the bounty by #djechlin
A lot of search on the w3/dom specs and didn't find anything final that specifically says that iframe should be reloaded while moving in the DOM tree, however I did find lots of references and comments in the webkit's trac/bugzilla/microsoft regarding different behavior changes over the years.
I hope someone will find anything specific regarding this issue, but for now here are my findings:
According to Ryosuke Niwa - "That's the expected behavior".
There was a "magic iframe" (webkit, 2010), but it was removed in 2012.
According to MS - "iframe resources are freed when removed from the DOM". When you appendChild(node) of existing node - that node is first removed from the dom.
Interesting thing here - IE<=8 didn't reload the iframe - this behavior is (somewhat) new (since IE>=9).
According to Hallvord R. M. Steen comment, this is a quote from the iframe specs
When an iframe element is inserted into a document that has a browsing context, the user agent must create a new browsing context, set the element's nested browsing context to the newly-created browsing context, and then process the iframe attributes for the "first time".
This is the most close thing I found in the specs, however it's still require some interpretation (since when we move the iframe element in the DOM we don't really do a full remove, even if the browsers uses the node.removeChild method).
Whenever an iframe is appended and has a src attribute applied it fires a load action similarly to when creating an Image tag via JS. So when you remove and then append them they are completely new entities and they refresh. Its kind of how window.location = window.location will reload a page.
The only way I know to reposition iframes is via CSS. Here is an example I put together showing one way to handle this with flex-box:
https://jsfiddle.net/3g73sz3k/15/
The basic idea is to create a flex-box wrapper and then define an specific order for the iframes using the order attribute on each iframe wrapper.
<style>
.container{
display: flex;
flex-direction: column;
}
</style>
<div class="container">
<div id="wrap1" style="order: 0" class="iframe-wrapper">
<iframe id="iframe1" src="https://google.com"></iframe>
</div>
<div id="warp2" style="order: 1" class="iframe-wrapper">
<iframe id="iframe2" src="https://bing.com"></iframe>
</div>
</div>
As you can see in the JS fiddle these order styles are inline to simplify the flip button so rotate the iframes.
I sourced the solution from this StackOverflow question: Swap DIV position with CSS only
Hope that helps.
If you have created the iFrame on the page and simply need to move it's position later try this approach:
Append the iFrame to the body and use a high z-index and top,left,width,height to put the iFrame where you want.
Even CSS zoom works on the body without reloading which is awesome!
I maintain two states for my "widget" and it is either injected in place in the DOM or to the body using this method.
This is useful when other content or libraries will squish or squash your iFrame.
BOOM!
Unfortunately, the parentNode property of an HTML DOM element is read-only. You can adjust the positions of the iframes, of course, but you can't change their location in the DOM and preserve their states.
See this jsfiddle I created that provides a good test bed. http://jsfiddle.net/RpHTj/1/
Click on the box to toggle the value. Click on the "move" to run the javascript.
This question is pretty old... but I did find a way to move an iframe without it reloading. CSS only. I have multiple iframes with camera streams, I dont like when they reload when i swap them. So i used a combination of float, position:absolute, and some dummy blocks to move them around without reloading them and having the desired layout on demand (resizing and all).
If you are using the iframe to access pages you control, you could create some javascript to allow your parent to communicate with the iframe via postMessage
From there, you could build login inside the iframe to record state changes, and before moving dom, request that as a json object.
Once moved, the iframe will reload, you can pass the state data into the iframe and the iframe listening can parse the data back into the previous state.
PaulSCoder has the right solution. Never manipulate the DOM for this purpose. The classic approach for this is to have a relative position and "flip" the positions in the click event. It's only not wise to put the click event on the body, because it bubbles from other elements too.
$("body").click(function () {
var frame1Height = $(frame1).outerHeight(true);
var frame2Height = $(frame2).outerHeight(true);
var pos = $(frame1).css("top");
if (pos === "0px") {
$(frame1).css("top", frame2Height);
$(frame2).css("top", -frame1Height);
} else {
$(frame1).css("top", 0);
$(frame2).css("top", 0);
}
});
If you only have content that is not cross-domain you could save and restore the HTML:
var htmlContent = $(frame).contents().find("html").children();
// do something
$(frame).contents().find("html").html(htmlContent);
The advantage of the first method is, that the frame keeps on doing what it was doing. With the second method, the frame gets reloaded and starts it's code again.
At least in some circumstances a shadow dom with slotting might be an option.
<template>
<style>div {outline:1px solid black; height:45px}</style>
<div><slot name="a" /></div>
<div><slot name="b" /></div>
</template>
<div id="shadowhost">
<iframe src="data:text/html,<button onclick='this.innerText+=`!`'>!</button>"
slot="a" height=40px ></iframe>
</div>
<button onclick="ifr.slot= (ifr.slot=='a') ? 'b' : 'a';">swap</button>
<script>
document.querySelector('#shadowhost').attachShadow({mode: 'open'}).appendChild(
document.querySelector('template').content
);
ifr=document.querySelector('iframe');
</script>
In response to the bounty #djechlin placed on this question, I have forked the jsfiddle posted by #matt-h and have come to the conclusion that this is still not possible.
http://jsfiddle.net/gr3wo9u6/
//this does not work, the frames reload when appended back to the DOM
function swapFrames() {
var w1 = document.getElementById('wrap1');
var w2 = document.getElementById('wrap2');
var f1 = w1.querySelector('iframe');
var f2 = w2.querySelector('iframe');
w1.removeChild(f1);
w2.removeChild(f2);
w1.appendChild(f2);
w2.appendChild(f1);
//f1.parentNode = w2;
//f2.parentNode = w1;
//alert(f1.parentNode.id);
}
I give up... All of your answers were just different ways of targeting the local element.
If you bothered to actually read what I was saying you would realise that it was not a problem with the code I already had, just that the code DID NOT work on IMG tags.
While faffing around trying to demonstrate my problem (and that none of your solutions did anything different to what was already happening) I found that I can achieve exactly what I want by applying a Grayscale filter to a DIV element placed over each image. The mouseover event then triggers an opacity change in the DIV element.
It is a little heavier that I wanted but it answered my ACTUAL question. The answer being:
Yes, there probably is a way to toggle class of IMG tags. But no, I am probably not going to find it here without causing arguments or being told i'm using "bad code". So yes, it IS easier and more efficient to target DIV elements.
By the way, page load times are about how large data packages are. Larger data packages (images, html/css/js documents, etc) take longer to download and so the page takes longer to load. The website I am trying to create proves this thesis, I have an almost complete and (almost) fully functional website with loads of 'clever' little effects all under 20mb, about 15mb of which is images. This website is clean and simple, is hosted on my Samsung Galaxy Tab 4 (using Papaya) and loads almost instantly.
THIS is what I meant by "I want this to be VERY lite". Thank you all for your attempts to help, it's just a shame that I couldn't get anyone to understand what was going on.
If you add onClick to image element you don't need to pass anything, you will receive MouseEvent which contains all information. You need target from event.
I suggest to not use onClick on element as it is not scalable, you have to add it to all elements. Better to add listener to wrapping/container element and then filter target by some attribute e.g data-something Please check fiddle
So you have wrapping element and you images:
<div class="images-container">
<img src="https://placeholdit.imgix.net/~text?txtsize=33&txt=350%C3%97150&w=350&h=150" data-toggleable class="thumb-gray thumb-color" />
<img src="https://placeholdit.imgix.net/~text?txtsize=33&txt=350%C3%97150&w=350&h=150" data-toggleable class="thumb-gray" />
<img src="https://placeholdit.imgix.net/~text?txtsize=33&txt=350%C3%97150&w=350&h=150" data-toggleable class="thumb-gray" />
</div>
and you attach listener to you wrapping element. It is best practice as you don't attach listeners to each element and same time you are able easily scale your solution
var imagesContainerEl = document.querySelector('.images-container');
imagesContainerEl.addEventListener('click', function(event) {
var element = event.target;
if (element.hasAttribute('data-toggleable')) {
element.classList.toggle('thumb-color');
}
});
The same code can be extended to support mouseover and mouseout. Check fiddle2. One function to rule them all and in the darkness bind them..
var imagesContainerEl = document.querySelector('.images-container');
imagesContainerEl.addEventListener('mouseover', onToggleImage);
imagesContainerEl.addEventListener('mouseout', onToggleImage);
function onToggleImage(event) {
var element = event.target;
if (element.hasAttribute('data-toggleable')) {
element.classList.toggle('thumb-color');
}
}
Also updated fiddle which shows how to make image grayscale/color
Is what you refer to in your question as
onClick="colorFunction(image1)"
an inline javascript event listener?
If so, try replacing it with:
onClick="colorFunction(this)"
and rewrite colorFunction() as:
function colorFunction(image) {
image.classList.toggle('thumb-color');
}
I am testing a webpage that has multiple forms in it.
When I use
client.frame({id:client.element('#frameId')});
I don't get any error, but when I try to interact with an element within that frame I get a RuntimeError telling me the element could not be located.
I have been looking out for literature about how the frame() method works but I don't have any luck.
I was also using webdriver.io and it looks like documentation is a bit wrong.
You can access frames:
1) via it's number on the page. For example the first frame met in HTML DOM is
client.frame(0), second client.frame(1) etc
2) via name attribute:
<frame name="test"></frame>
client.frame('test')
3) find the element with client.element('css_selector'), then in a callback pass the returned value to the .frame()
The way to go to a new frame is:
client.frame(<id of frame here>)
What you have should work too though. Try doing a client.waitForExist on an element that only exists on the frame, instead of just switching to frame and immediately trying to interact with element in that frame, as you may be firing your interaction event before selenium has had a chance to fully switch to the frame:
client.frame(<id of frame here>
client.waitForExist(<id of some css element that only exists in the frame>)
client.frame(<name_of_frame>) worked.
I tried using a selector like #idOfSelector but it didn't seem to work.
This works for me
const frameValue = browser.element('frame_selector').value;
browser.frame(frameValue);
Hopefully, it works for you.
I'm having an issue with iframes and IDs.
I currently have an Iframe with no ID attached to as its generated by another websites javascript. So I quickly wrote a Jquery script to give IDs to Iframes on load of page, and it worked successfully. Problem is however, it applies the ID to ALL the Iframes on the page instead of specifically the one I want.
This is what I have.
<script>
$(document).ready(function () {
$("iframe").attr({
id: "iframeid1"
});
});</script>
Is there a method with Jquery to 'search and replace' something specific on the page? For example
Search for:
<iframe allowtransparency="true" frameborder="0" scrolling="no" width="160" height="600"
Replace with:
<iframe id="iframeid1" allowtransparency="true" frameborder="0" scrolling="no" width="160" height="600"
Any help is appreciated! Thanks in advance!
If you know it is the nth iframe on the page:
$("iframe")[n].setAttribute('id', 'iframe1');
EDIT: You could also use attribute selectors:
$("iframe[allowtransparency=true][frameborder=no][etc=etc]").attr({id: 'iframe1'});
It depends on if there is a unique way of finding the iframe you want. For example, is it the only one with width = 160 and height = 600? Or is it always the Xth iframe on the page? Is it always located in the same spot in the page?
Here are some queries as examples for all 3 scenarios:
// if the width/height combination is unique...
var iframe = $('iframe[width=160][height=600]');
// if it is always, say, the 3rd iframe on the page
var iframe = $('iframe:eq(2)'); // 0-based index
// if it is always the only iframe in a div with an id of "iframeContainer"...
var iframe = $('#iframeContainer').find('iframe');
Then you can set the attribute like you said:
iframe.attr('id', 'iframeid1');
if the iframe is wrapped inside a div, with a ID, than you can do:
<script>
$(document).ready(function () {
$("#divID iframe").attr({
id: "iframeid1"
});
});</script>
If you know where your iframe is at you can get it by index position.
If you have 4 iframes on the page and you're looking for the third (with respect to the DOM), you can do this:
$("iframe").get(3).attr({
id: "iframeid1"
});
You can use selectors to find an iframe tag with specific attributes. But you need to be able to uniquely identify the specific iframe you want from attribute values, not HTML search.
As an example:
$('iframe[width="160"][height="600"]').attr("id", "iframeid1");
This would select iframe tags with width=160 and height=600. You can add more attributes if needed to uniquely select your particular iframe.
Perhaps the best option would be to use the DOM structure around the iframe to identify which one you want. You could use parent object identifiers, ordering of the tags, etc.. whatever helps uniquely identify the one you want.