In my test, given 2 document, A and B. In A document, there is an iframe, the iframe source is B document. My question is how to modify B document certain scope of variable?
Here is my code: A document
<html lang="en" ng-app="">
<head>
<meta charset="utf-8">
<title>Google Phone Gallery</title>
<script type='text/javascript' src="js/jquery-1.10.2.js"></script>
<script type='text/javascript' src="js/angular1.0.2.min.js"></script>
<script>
var g ;
function test($scope,$http,$compile)
{
$scope.tryget = function(){
var iframeContentWindow = $("#iframe")[0].contentWindow;
var iframeDOM = $("#iframe")[0].contentWindow.document;
var target = $(iframeDOM).find("#test2");
var iframeAngular = iframeContentWindow.angular;
var iframeScope = iframeAngular.element("#test2").scope();
iframeScope.parentcall();
iframeContentWindow.angular.element("#test2").scope().tempvalue = 66 ;
iframeScope.tempvalue = 66;
iframeContentWindow.tt = 22;
iframeScope.parentcall();
console.log(iframeScope.tempvalue);
console.log(angular.element("#cont").scope());
}
}
</script>
</head>
<body>
<div ng-controller="test">
<div id="cont" >
<button ng-click="tryget()">try</button>
</div>
</div>
<iframe src="test2.html" id="iframe"></iframe>
</body>
</html>
My B document:
<!doctype html>
<html lang="en" ng-app="">
<head>
<meta charset="utf-8">
<title>Google Phone Gallery</title>
<script type='text/javascript' src="js/jquery-1.10.2.js"></script>
<script type='text/javascript' src="js/angular1.0.2.min.js"></script>
<script>
var tt =11;
function test2($scope,$http,$compile)
{
console.log("test2 controller initialize");
$scope.tempvalue=0;
$scope.parentcall = function()
{
$scope.tempvalue = 99 ;
console.log($scope.tempvalue);
console.log(tt);
}
}
</script>
</head>
<body>
<div ng-controller="test2" id="test2">
<div id="cont" >
<button ng-click="parentcall()">get script</button>
</div>
{{tempvalue}}
</div>
</body>
</html>
Note: Actually there is some way to do it, which i feel it like a hack instead of proper way to get it done:
that is create a button in b Document, and then bind with angularjs ng-click. After that A document jquery "trigger" click on button.
To access and communicate in two directions (parent to iFrame, iFrame to parent), in case they are both in the same domain, with access to the angular scope, try following those steps:
*You don’t need the parent to have reference to angularJS library…
Calling to child iFrame from parent
1.Get child iFrame element from the parent (link to answer):
document.getElementById("myIframe").contentWindow
2.Access the scope of the element:
document.getElementById("myIframe").contentWindow.angular.element("#someDiv").scope()
3.Call the scope’s function or property:
document.getElementById("myIframe").contentWindow.angular.element("#someDiv").scope().someAngularFunction(data);
4.Call $scope.$apply after running the logic of the function/updating the property (link to Mishko’s answer):
$scope.$apply(function () { });
Another solution is to share the scope between the iFrames, but then you need angular in both sides: (link to answer and example)
Calling parent from child iFrame
Calling the parent function:
parent.someChildsFunction();
Will update also on how to do it cross domain if it is necessary..
You should be able to get parent scope from iFrame:
var parentScope = $window.parent.angular.element($window.frameElement).scope();
Then you can call parent method or change parent variable( but remember to call parentScope.$apply to sync the changes)
Tested on Angular 1.3.4
The best way in my mind to communicate with the iframe is using window.top. If you want your iframe to get your parent's scope, you can set window.scopeToShare = $scope; within your controller and it becomes accessible for the iframe page at window.top.scopeToShare.
If you want your parent to get the iframe scope, you can use
window.receiveScope = function(scope) {
scope.$on('event', function() {
/* Treat the event */
}
};
and within the iframe controller call window.top.giveRootScope($rootScope);
WARNING: If you are using this controller multiple times, make sure to use an additional ID to identify which scope you want.
This one is quite simple and works for me:
in the controller code of iframe page:
$window.parent.window.updatedata($scope.data);
in the parent page controller code:
window.updatedata = function (data) {
$scope.$apply(function () {
$scope.data = data
}
}
Related
Is there any way possible to load a Google Translate widget in the sidebar and one in the footer, for example.
Every way I've tried has only loaded so that both appear in the location of the first instance on the page.
After a bit of tinkering I kinda felt obligated to solve the puzzle! You can skip to the good part by checking out the jsfiddle: (it works as of now but knowing google it might not tomorrow)
http://jsfiddle.net/melfy/15zr6ov0/
Lets begin:
First google translate is loaded and adds a listener for a select box it adds to the DOM after you call the right element but we need that change event to call a change for a select box we're going to clone from the original one to get google to update the translation, this gets a bit messy as we over take the prototype (which is usually bad practice)
Start by adding your header element:
<div id="google_translate_element"></div>
Then we add our footer element:
<div id="google_translate_element2"></div>
Next we pull in the google translator
<script type="text/javascript" src="https://translate.google.com/translate_a/element.js?cb=googleTranslateElementInit"></script>
Now we get to the good part:
<script type="text/javascript">
// store google translate's change event
trackChange = null;
pageDelayed = 3000;
// overwrite prototype to snoop, reset after we find it (keep this right before translate init)
Element.prototype._addEventListener = Element.prototype.addEventListener;
Element.prototype.addEventListener = function(a,b,c) {
reset = false;
// filter out first change event
if (a == 'change'){
trackChange = b;
reset = true;
}
if(c==undefined)
c=false;
this._addEventListener(a,b,c);
if(!this.eventListenerList)
this.eventListenerList = {};
if(!this.eventListenerList[a])
this.eventListenerList[a] = [];
this.eventListenerList[a].push({listener:b,useCapture:c});
if (reset){
Element.prototype.addEventListener = Element.prototype._addEventListener;
}
};
function googleTranslateElementInit() {
new google.translate.TranslateElement({ pageLanguage: 'en' }, 'google_translate_element');
let first = $('#google_translate_element');
let second = $('#google_translate_element2');
let nowChanging = false;
// we need to let it load, since it'll be in footer a small delay shouldn't be a problem
setTimeout(function(){
select = first.find('select');
// lets clone the translate select
second.html(first.clone());
second.find('select').val(select.val());
// add our own event change
first.find('select').on('change', function(event){
if (nowChanging == false){
second.find('select').val($(this).val());
}
return true;
});
second.find('select').on('change', function(event){
if (nowChanging){
return;
}
nowChanging = true;
first.find('select').val($(this).val());
trackChange();
// give this some timeout incase changing events try to hit each other
setTimeout(function(){
nowChanging = false;
}, 1000);
});
}, pageDelayed);
}
</script>
You can change the pageDelayed variable to trigger quicker or slower but if it's in your footer, bumping it up to delay longer may help it work more efficiently depending on your page load
Unfortunately, you can not have the widget be loaded more than once in a single page. Google just doesn't allow for that. One potential workaround would be putting the code in an iFrame and then putting two iFrames onto your webpage.
Create a file called iframe.html
<html>
<head>
<script type="text/javascript" src="//translate.google.com/translate_a/element.js?cb=googleTranslateElementInit">
</head>
<body>
<div id="google_translate_element"></div>
<script type="text/javascript">
function googleTranslateElementInit(){
new google.translate.TranslateElement({pageLanguage: 'en'}, 'google_translate_element');
}
</script>
</body>
</head>
</html>
In your other file put code something like this:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>Google Translate</title>
<script type="text/javascript" src="//translate.google.com/translate_a/element.js?b=googleTranslateElementInit"></script>
</head>
<body>
<div id="header" style="background-color: red;">
<iframe src="iframe.html"></iframe>
<strong>A</strong>
</div>
<div id="footer" style="background-color: blue;">
<iframe src="iframe.html"></iframe>
<strong>B</strong>
</div>
</body>
</html>
I have a file call text.js and it has
var Text = function(canvas){
var textField = $('#textField'),
addTextButton = $('#addText');
var init = function(){
addTextButton.click(function(){
alert('won"t work?')
});
},
resetTextField = function(){
// it work if I put the selector here like var textField = $('#textField'),
textField.val(''); // won't work
};
return{
init:init
}
}();
It's included in my index.html. In there I do init like
$(function(){
Text.init();
}());
The problem is the even't can't be fired. I think I messed up something.
The code in Text is run immediately, and returns the object with init on it. If you run that code before the elements it looks up exist, for instance:
<!doctype html>
<html>
<head>
<!-- ... --->
<script src="text.js"></script><!-- Problem here -->
<script>
$(function(){
Text.init();
}());
</script>
</head>
<body>
<!-- ... --->
<input id="textField"><input id="addText" type="button" value="Add">
<!-- ... -->
</body>
</html>
... you'll end up with empty jQuery objects in textField and addTextButton.
Separately, you're also running the function you're trying to pass ready immediately (and then passing undefined into ready), the problem is here:
$(function(){
Text.init();
}());
// ^^---------- problem
You don't want those (). You want to pass the function into ready:
$(function(){
Text.init();
}); // <== Note no ()
If you're going to have the init method, it would be best to put all your initialization inside it rather than putting it in two places:
var Text = function(canvas){
var textField, addTextButton;
var init = function(){
textField = $('#textField');
addTextButton = $('#addText');
addTextButton.click(function(){
alert('won"t work?')
});
},
resetTextField = function(){
// it work if I put the selector here like var textField = $('#textField'),
textField.val(''); // won't work
};
return{
init:init
}
}();
Note, though, that if you follow the usual best practice of putting your scripts at the end of the document, just prior to the closing </body> tag, the elements defined above that will exist and be available, which would make using ready (and init) unnecessary. So if you control where the script tags go, that's an option.
So for instance:
<!doctype html>
<html>
<head>
<!-- ... --->
</head>
<body>
<!-- ... -->
<input id="textField"><input id="addText" type="button" value="Add">
<!-- ... -->
<script src="text.js"></script>
<script>
$(function(){
Text.init();
});
</script>
</body>
</html>
You are invoking the function once defined using () at a point where DOM is not loaded. Thus, all selectors return zero nodes.
var Text = function(canvas){
// ...
}();
^^
Remove that. And when you call it, you need to instance the function first, and keep that instance reference (if you wish to).
var text = new Text();
text.init();
console.dir(element[0].parentElement)
console.dir(element[0].parentElement.offsetTop)
I want to get parent's element offset in angularjs.
I try to get information about parentElement.
First row about parentElement has offsetTop like 385.
But when I check parentElement.offsetTop like second row, it return only 0.
I try to using parentElement.getBoundingClientRect(), but it return 0, too.
How cant I get parent's element offset?
if u put ur code in a link function AND the size or position of element's parentElement depends on how you render your html based on scope variable, u are not able to get the correct offsetTop in the link function directly.
Will u try the following way by getting it after current angular digest:
scope.$evalAsync(function(){
console.log(element[0].parentElement.offsetTop);
})
Check if this works.
Here is an example using jquery to get the offset of a parent (I will also create an example of how to do this with a custom attribute directive in angular shortly. Here is a quick example:
HTML
<div>
<div id="mydiv"></div>
</div>
JS
var element = $('#mydiv');
var parentOffset = $(element).offset();
console.log(parentOffset);
ANGULAR
HTML
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" ng-app="myApp">
<head runat="server">
<title></title>
</head>
<body ng-controller="myController">
<div>
<div parent-offset>Test</div>
</div>
<script src="scripts/jquery.js"></script>
<script src="scripts/angular.js"></script>
<script src="scripts/app.js"></script>
</body>
</html>
JS
(function () {
angular.module('myApp', [])
.controller('myController', function ($scope) {
})
.directive('parentOffset', function () {
return {
restrict: 'A',
link: function (scope, element, attr) {
console.log(element.parent().offset());
}
}
})
}());
I want create animation on mousemove in angularjs. I found example
https://docs.angularjs.org/api/ng/directive/ngMousemove
but i want to run function.
So inside of body
<body data-ng-mousemove="squareRotate()">
And js:
$scope.squareRotate = function(){
alert();
};
but i cant make it work. How can i manage it without puting it inside of controller ?
Since you didn't post your complete code, one can only guess. I am guessing that either the place of your body is really small, so you don't really move the move over the body, or angularjs application and controller are not properly initialised.
In order to give the html and body enough room, use the following:
html, body {
width: 100%;
height: 100%;
}
I created a working demo in fiddle. The only difference is that I don't use alerts, but a counter, which increases, when you move your mouse over the field.
testApp.directive('testDir', function () {
return function (scope, element) {
var el = element[0];
el.addEventListener(
'mousemove',
function () {
alert('test');
},
false
);
}
});
You may try it here: http://jsfiddle.net/AfNH9/4/
If you meant something different, please specify further.
Use directive:
I update the example to use a directive. The directive is bound to the body tag and uses an eventListener on "mousemove". If you move the mouse over the "Result" window in fiddle, you will see the alert window. http://jsfiddle.net/AfNH9/6/
Please see here : http://plnkr.co/edit/tpl:FrTqqTNoY8BEfHs9bB0f?p=preview
HTML:
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js#1.2.x" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.19/angular.min.js" data-semver="1.2.19"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl" data-ng-mousemove="squareRotate()">
<p>Hello {{name}}!</p>
</body>
</html>
js:
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
$scope.squareRotate = function(){
alert();
};
});
Good Day!
is it possible when i open new window from parent window, a javascript will execute on the new WINDOW?
Like for example if the new window completely load, a textfield will change a value.
Thanks
get the parameter from new window onLoad() event to parent window, notifying that new window has been load.
I hope this will solve your question.
Here's an example for putting a function in a child window then let the child window runs it. The showTitle function will simply show the current document title. The child page will wait for a specific function and calls it when it arrives.
parent.html:
<html>
<head>
<title>parent</title>
</head>
<body>
<script>
function showTitle() {
alert('Page Title = '+this.document.title);
}
function childLoaded() {
showTitle();
var childWnd=document.getElementById('fchild').contentWindow;
if (!childWnd) return;
childWnd.newFunc=showTitle;
}
</script>
parent<br />
<iframe id=fchild src="child.html" width=300 height=300 onload="childLoaded()"></iframe>
</body>
</html>
child.html:
<html>
<head>
<title>child</title>
</head>
<body>
<script>
var waiter,newFunc=null;
function waitFunc() {
if (newFunc) {
clearInterval(waiter);
newFunc();
}
}
waiter=setInterval(waitFunc, 1000);
</script>
child
</body>
</html>