Data Attributes are a safe place to store strings onto a DOM element, because you're guaranteed that your property-naming will never collide with a future property that gets added to the DOM specification.
However, what if you want to bind a non-JSON javascript object to an DOM element as a property?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Bind Custom Object to DOM Element</title>
<style>
div{ border: solid #333333 1vmin; padding:1vmin;
display:inline-block; font-size:4vmin}
</style>
<script type="text/javascript">
function main()
{
let obj = {}
obj.name = "Lonnie";
let div = document.createElement("div");
div.textContent = "Click Me";
div.myCustomObject = obj;
div.addEventListener('click',function()
{
alert(this.myCustomObject.name);
});
document.body.appendChild(div);
}
window.addEventListener('load', main);
</script>
</head>
<body>
</body>
</html>
Data attributes are designed to hold strings, not objects. Is it ok to use them for nonJSON object-properties, or does the specification recommend a different location for adding object-properties to DOM nodes?
Data attributes are designed to hold strings, not objects.
But you are not using attributes. Therefore you can store everything you want under a regular property.
Is it ok to use them for nonJSON object-properties,
Sure.
or does the specification recommend a different location for adding object-properties to DOM nodes?
No, it does not. the DOM spec is language neutral, therefore it doesn't really describe how objects in JS behave that implement that spec.
I would use a WeakMap solution using ES6:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Bind Custom Object to DOM Element</title>
<style>
div{ border: solid #333333 1vmin; padding:1vmin;
display:inline-block; font-size:4vmin}
</style>
<script type="text/javascript">
async function main()
{
let obj = {}
obj.name = "Lonnie";
let obj2 = {}
obj2.name = "Mark"
let div = document.createElement("div");
div.textContent = "Click Me";
div.myCustomObject = obj;
const wm = new WeakMap();
wm.set(div, obj);
wm.set(div, obj2);
console.log(wm.get(div)); // "my value"
div.addEventListener('click',function()
{
alert(wm.get(div).name);
});
document.body.appendChild(div);
}
window.addEventListener('load', main);
</script>
</head>
<body>
</body>
</html>
This method ensures there are no clashes between property names.
Personally I don’t see any issue with binding it to an attribute however and there doesn’t seem to be any advice against doing so, it’s the data attributes that are meant to contain strings
Related
I have an AgentClass with the method this.move. It's working with static objects but when I create new HTML Objects via .append(), I can't use them with my this.move method.
All the new objects have an ID and I want to animate them with the move method.
I often read "live, on, ..." but they all need an event... I don't have such an event on them. They move directly. I tried something like that:
$('.agents').on("load", Agent.move());
But that isn't working... Any ideas?
Codesinppet:
var Agent = function(agentType, xTarget, yTarget) {
...
this.move = function() {
var id = this.agentId;
$('.agent#'+id).animate({
left:"200px"
}, 1000);
}
}
And I append them after this like this:
for (deployed = 0; deployed <= agents; deployed++) {
$('.agents').append('<div class="agent" id="'+deployed+'"></div>');
}
It would be awesome if someone could help me!?
You can use .clone(true)
A Boolean indicating whether event handlers and data should be copied along with the elements. The default value is false.
var agents = 6;
for (deployed = 0; deployed <= agents; deployed++) {
$element = $('<div class="agent" id="'+deployed+'"></div>').clone(true);
$('.agents').append($element);
}
.agent {
height:50px;
width:50px;
background-color:yellow;
margin-bottom:10px
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!DOCTYPE html>
<html>
<head>
<title>Agent</title>
</head>
<body>
<div class="agents">
</div>
</body>
</html>
But for maximum optimization event is better to use an event handler "on" to monitor the items that will be added after reloading the DOM .
This allocates less memory
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
}
}
So I'm fairly new to javascript, and I'm trying to use a simple if...else statement to toggle between showing and hiding a div element. My HTML page looks like this:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Test</title>
<style type="text/css">
#fluffy {
height:200px;
width:200px;
background-color:yellow;
display:block;
}
#pepper {
height:200px;
width:200px;
background-color:green;
display:block;
}
</style>
</head>
<body>
<div id="fluffy"></div>
<div id="pepper" onclick="changeState()"></div>
<script type="text/javascript">
function changeState() {
var divState = document.getElementById('fluffy').style.display;
if (document.getElementById('fluffy').style.display == 'block') {
document.getElementById('fluffy').style.display = 'none';
}
else if (document.getElementById('fluffy').style.display == 'none') {
document.getElementById('fluffy').style.display = 'block';
} else {
alert('test error message');
}
}
</script>
</body>
</html>
When I load the page in my browser, I receive an alert box containing 'test error message'. My original code had document.getElementById('fluffy').style.display stored in a variable called divState, but this didn't even give me an alert box, it didn't do anything. I get the feeling I'm using == wrong or something, but I'm really not sure. I've used ===, and when that didn't work I switched to ==, which is in the code above.
If anyone knows what exactly I'm doing wrong, I would appreciate the help.
Thanks, Harold
Alright, it looks like you guys fixed my problems. I can't thank you enough, and I will definitely look into jQuery!
Try changing the onclick to be this
<div id="pepper" onclick="changeState"></div>
document.getElementById('fluffy').style.display is ''. Since you're setting styles with a stylesheet, you'll have to use getComputedStyle (plus friends for cross-browser compatibility). You can find an example cross-browser implementation in the answer to Get all computed style of an element.
I know, you're trying to learn JavaScript what I also want to encourage, but with jQuery, this whole stuff would be a one-liner plus crossbrowser-friendly etc.
<div id="fluffy"></div>
<div id="pepper"></div>
The <script> contains just:
$("#pepper").click(function () { $("#fluffy").toggle(); });
Try it out at JSFiddle.
When the page first loads, the div doesn't have any inline styles. element.style reads inline styles only.
You will need to render the div with style="display:block;" or if you can't/don't want to do that, look into getComputedStyle options for your supported browsers
Use computed style:
<div id="fluffy"></div>
<div id="pepper" onclick="changeState()"></div>
<script type="text/javascript">
function changeState() {
var fluffy = document.getElementById('fluffy');
var divState = window.getComputedStyle(fluffy).display;
if (divState == 'block') {
fluffy.style.display = 'none';
}
else if (divState == 'none') {
fluffy.style.display = 'block';
} else {
alert('test error message');
}
}
</script>
jsFiddle
I tought $('#my_id1') was the same thing as document.getElementById('my_id1'). But it is parently not. What is the difference?
(function( $ ) {
$.fn.simple_hide_function = function() {
var $t = this;
$t.hide();
};
})( jQuery );
$(window).load(function () {
var $div1 = $('#my_id1');
var $div2 = document.getElementById('my_id2');
$div1.simple_hide_function(); // this is working
$div2.simple_hide_function(); // but this is not working
});
Adding example to make it more clear:
<!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">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Untitled Document</title>
</head>
<body>
<div id="my_id1" style="height:100px;background:#f00">div1</div>
<div id="my_id2" style="height:100px;background:#f00">div2</div>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script>
(function( $ ) {
$.fn.simple_hide_function = function() {
var $t = this;
$t.hide();
};
})( jQuery );
$(window).load(function () {
var $div1 = $('#my_id1');
var $div2 = document.getElementById('my_id2');
$div1.simple_hide_function();
$div2.simple_hide_function();
});
</script>
</body>
</html>
Difference is that first one returns a jquery object while the second returns a DOM element.
But these statements are equivalent:
document.getElementById('my_id2') <-> $('#my_id1').get(0)
or
document.getElementById('my_id2') <-> $('#my_id1')[0]
The first returns a jQuery object with that div as its only member. You can use jQuery functions on the object to manipulate it.
The second returns a DOMElement using the browser's built-in methods.
$('#my_id1') // Returns a jQuery object
And
getElementById('my_id1') // Returns a DOM object.
To get the DOM object of a jQuery object, you can call:
$('#my_id1').get()
jQuery can match more than one object with a selector, so to get the second matching DOM element:
$('#my_id1').get(1) // 1 = item #2 (zero-based index)
And to get matching DOM elements from the END of the collection, you can use a negative number, the distance from the end of the matched elements you want to retrieve, so -1 gets the last item.
$('#my_id1').get(-1) // gets the last item of the matched elements
Use my_id1:
var $div2 = document.getElementById('my_id1');
According to me, there is difference in its rendering in Browsers.
As if we do not use document. This will not work in IE browsers.
But only work in other browsers.
After the page loads the color of the label (which is "enter your name") should change to red.But the color of the label remains the same.Why is this so ?
SCRIPT
window.onload = startScript;
function startScript() {
if( document.getElementById("text_field").value === "me") {
var allTags = document.getElementsByTagName("label");
allTags.className = "inserter";
}
}
HTML
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Untitled Document</title>
<script type="text/javascript" src="inserter.js">
</script>
<style type="text/css">
#import url("inserter.css");
</style>
</head>
<body bgcolor="#99FFFF">
<form>
<label>Enter your name<input type="text" id="text_field" value="me" />
</label>
</form>
</body>
</html>
CSS
#charset "utf-8";
/* CSS Document */
.inserter {
color:#F00;
}
Now since the value is equal to me class name "inserter" is dynamically inserted to the label element and the color should appear to be red.
Why doesn't this happen ?
I think you need to loop through allTags:
var allTags = document.getElementsByTagName("label");
for (var i = 0; i < allTags.length; i++) {
allTags[i].className = 'inserter';
}
If you can though use jQuery, it makes life so much easier!
UPDATE to add jQuery equivalent of your code:
if ($('#text_field').val() === 'me') {
$('label').addClass('inserter');
}
It's just so much tidier
The getElementsByTagName method returns an array containing each of the DOM elements that match that tag. Calling allTags.className = 'inserter' assigns a value to the 'className' property of the array itself, not to each of the elements within that array. You'll need to iterate through it, and assign the className to each tag individually.
As others have already mentioned getElementsByTagName() returns an array with elements but also you should add brackets to the first row where you call startScript() to identify it as a function.
This example works; http://jsfiddle.net/aQASU/
Try this through jquery.
allTags.addClass("inserter");