User Definable Attributes on HTML Elements? - javascript

What I need to do is be able to store some piece of data about an element.
For example, lets say I have a list item <li>, and I want to store some data about it in the element, like "This is element 1 from XYZ".
The only way I know how to do this (which I don't want to do if I can avoid) is this:
<li id='item1'>
Item 1
<!--This is element 1 from XYZ-->
</li>
<script>
// read it something like this
var e = document.getElementById('item1').innerHTML;
// then parse out the comment, etc.
// --
</script>
What I really want is something like this (which I am fairly certain is not possible):
// example
<li id='item1' userdata='This is element 1 from XYZ'>Item 1</li>
.. of course, I would like to be able to access it somehow via javasscript.
Alternatively, is there some other way to achieve this?
Thanks -

You can access your userdata="" attribute from JavaScript. Just do:
var theData = document.getElementById('item1').getAttribute('userdata');
If you want to do it the HTML5 way, then you would use attributes named data-*, e.g.:
<li id='item1' data-foo='This is element 1 from XYZ'>Item 1</li>
that way it will still be valid (i.e., it'll make you feel better for not using an invalid attribute). New browsers will support accessing the data-* attributes like so:
var theData = document.getElementById('item1').data.foo;
but I don't think that is implemented widely enough to rely upon yet.
If you do want to store the data in a comment (although I'd advise going the attribute route instead) you could do something like:
var e = document.getElementById('item1');
var n = e.firstChild;
while (n && n.nodeType != Node.COMMENT_NODE) {
n = n.nextSibling;
}
// now n.nodeValue will have the comment contents
(No guarantees about whether IE likes any of the above.)

You can't add arbitrary elements to HTML. Well you can but it won't be valid and beheaviour is undefined. For this kind of thing I tend to use jQuery. It has a data() call that can add arbitrary data to an element. I believe it encodes it in the class attribute but the implementation is not important.
You could do this yourself but why bother? It's easy to get wrong by putting the wrong characters in, not correctly escaping/unescaping data or inadvertently destroying informatino. Instead just do:
$("#item1").data({source: "Thsi is element 1 from XYZ"});

Since you can accept adding comments, a better solution would be to add a span element with the content you wanted..
<span class="data">.....</span>
you define your data class to have display:none and it is invisible ...
this way you can have access to it with the normal DOM traversing methods..

You can use setUserData() and getUserData() function
http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html#Node3-setUserData
For example:
<html>
<head>
<script type="text/javascript">
function set(){
var a = document.getElementById("testElement");
a.setUserData("testData", "Some text", null);
}
function get(){
var a = document.getElementById("testElement");
alert(a.getUserData("testData"));
}
</script>
</head>
<body>
<span id="testElement"/>
<form>
<input type="button" value="setUserData" onclick="set()"/>
<input type="button" value="getUserData" onclick="get()"/>
</form>
</body>

If you don't need the HTML to be valid, you can make any attribute you want, and you can access it in Javascript by calling the getAttribute method.

You can add a nested invisible element with an id and your data as the innerText. Use the style attribute to make sure it's invisible.
But it all really depends on what you're trying to achieve. Could you elaborate more?

// If you want to include data in the html why not give it its own node?
.hiddendata{
display: none
}
<li id= 'item1'>
Item 1
<span class= "hiddendata"> This is element 1 from XYZ</span>
</li>
function readHiddenData(who){
var A= [], n= who.firstChild;
while(n){
if(n.className= 'hiddendata'){
A[A.length]= n.textContent || n.innerText || '';
}
n= n.nextSibling;
}
return A;
}
function showHiddenData(who){
var n= who.firstChild;
while(n){
if(n.className= 'hiddendata'){
n.style.display= "inline"
}
n= n.nextSibling;
}
}

Related

show all the values with .html [duplicate]

Lets say I have an empty div:
<div id='myDiv'></div>
Is this:
$('#myDiv').html("<div id='mySecondDiv'></div>");
The same as:
var mySecondDiv=$("<div id='mySecondDiv'></div>");
$('#myDiv').append(mySecondDiv);
Whenever you pass a string of HTML to any of jQuery's methods, this is what happens:
A temporary element is created, let's call it x. x's innerHTML is set to the string of HTML that you've passed. Then jQuery will transfer each of the produced nodes (that is, x's childNodes) over to a newly created document fragment, which it will then cache for next time. It will then return the fragment's childNodes as a fresh DOM collection.
Note that it's actually a lot more complicated than that, as jQuery does a bunch of cross-browser checks and various other optimisations. E.g. if you pass just <div></div> to jQuery(), jQuery will take a shortcut and simply do document.createElement('div').
EDIT: To see the sheer quantity of checks that jQuery performs, have a look here, here and here.
innerHTML is generally the faster approach, although don't let that govern what you do all the time. jQuery's approach isn't quite as simple as element.innerHTML = ... -- as I mentioned, there are a bunch of checks and optimisations occurring.
The correct technique depends heavily on the situation. If you want to create a large number of identical elements, then the last thing you want to do is create a massive loop, creating a new jQuery object on every iteration. E.g. the quickest way to create 100 divs with jQuery:
jQuery(Array(101).join('<div></div>'));
There are also issues of readability and maintenance to take into account.
This:
$('<div id="' + someID + '" class="foobar">' + content + '</div>');
... is a lot harder to maintain than this:
$('<div/>', {
id: someID,
className: 'foobar',
html: content
});
They are not the same. The first one replaces the HTML without creating another jQuery object first. The second creates an additional jQuery wrapper for the second div, then appends it to the first.
One jQuery Wrapper (per example):
$("#myDiv").html('<div id="mySecondDiv"></div>');
$("#myDiv").append('<div id="mySecondDiv"></div>');
Two jQuery Wrappers (per example):
var mySecondDiv=$('<div id="mySecondDiv"></div>');
$('#myDiv').html(mySecondDiv);
var mySecondDiv=$('<div id="mySecondDiv"></div>');
$('#myDiv').append(mySecondDiv);
You have a few different use cases going on. If you want to replace the content, .html is a great call since its the equivalent of innerHTML = "...". However, if you just want to append content, the extra $() wrapper set is unneeded.
Only use two wrappers if you need to manipulate the added div later on. Even in that case, you still might only need to use one:
var mySecondDiv = $("<div id='mySecondDiv'></div>").appendTo("#myDiv");
// other code here
mySecondDiv.hide();
if by .add you mean .append, then the result is the same if #myDiv is empty.
is the performance the same? dont know.
.html(x) ends up doing the same thing as .empty().append(x)
Well, .html() uses .innerHTML which is faster than DOM creation.
.html() will replace everything.
.append() will just append at the end.
You can get the second method to achieve the same effect by:
var mySecondDiv = $('<div></div>');
$(mySecondDiv).find('div').attr('id', 'mySecondDiv');
$('#myDiv').append(mySecondDiv);
Luca mentioned that html() just inserts hte HTML which results in faster performance.
In some occassions though, you would opt for the second option, consider:
// Clumsy string concat, error prone
$('#myDiv').html("<div style='width:'" + myWidth + "'px'>Lorem ipsum</div>");
// Isn't this a lot cleaner? (though longer)
var newDiv = $('<div></div>');
$(newDiv).find('div').css('width', myWidth);
$('#myDiv').append(newDiv);
Other than the given answers, in the case that you have something like this:
<div id="test">
<input type="file" name="file0" onchange="changed()">
</div>
<script type="text/javascript">
var isAllowed = true;
function changed()
{
if (isAllowed)
{
var tmpHTML = $('#test').html();
tmpHTML += "<input type=\"file\" name=\"file1\" onchange=\"changed()\">";
$('#test').html(tmpHTML);
isAllowed = false;
}
}
</script>
meaning that you want to automatically add one more file upload if any files were uploaded, the mentioned code will not work, because after the file is uploaded, the first file-upload element will be recreated and therefore the uploaded file will be wiped from it. You should use .append() instead:
function changed()
{
if (isAllowed)
{
var tmpHTML = "<input type=\"file\" name=\"file1\" onchange=\"changed()\">";
$('#test').append(tmpHTML);
isAllowed = false;
}
}
This has happened to me . Jquery version : 3.3.
If you are looping through a list of objects, and want to add each object as a child of some parent dom element, then .html and .append will behave very different. .html will end up adding only the last object to the parent element, whereas .append will add all the list objects as children of the parent element.

How to move onclick code to one place using something like class. Repeated code at various html tags

onclick="triggersTracking($(this).attr('a'),$(this).attr('b'),$(this).attr('c'),Enum.BtnSellerView)"
I have this line at various HTML tags/buttons. I want to move this code to one place for better maintainability. The problem is with third/last attribute i am passing since its Enum, it has different values being passed from different tag elements.
How can i move it to one common place where it would get invoke. For example I could have made a class if i have had just these (this).attr since its common for every tag.
You can do like
Give all the elements a common class name
Then add a data attribute, "data-enum" to each tag with corresponding value.
Then you can write the code like this,
$(".className").click(function () {
var a = $(this).attr('a');
var b = $(this).attr('b');
var c = $(this).attr('c');
var enum = $(this).data('enum');
});
You can use jquery to get this:
$("body").on("click", "someClass", function() {
//code here
});
you don't need to write $(this).attr('a'),$(this).attr('b'),$(this).attr('c')
again and agin on every onclick just paas this object and get them all in function like :
onclick="triggersTracking(this,Enum.BtnSellerView)"
function triggersTracking(obj,enumVal){
// get these values here by obj (no repetitive code needed in every onclick )
$(obj).attr('a')
$(obj).attr('c')
$(obj).attr('b')
}
Do some thing like this
.click()
.on()
.data()
if you use attributes like data-enum , data-e....
so use $(this).data() it will return all attributes in JSON which is starting
from data-
$('.click').click(function(e) {
console.log($(this).data())
$('body').append($(this).attr('a'))
})
// if you have dynamic html tag then go for .on
$('body').on('click','.click',function(){
//callback
console.log($(this).data())
$('body').append($(this).attr('a'))
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p class="click" a="i am a attr of p" b="i am b" data-a="i am data a">i am p</p>
<h2 class="click" a="i am a attr of h2">i am h2</h2>

how to use html script template correctly

I have to generate some li element automatically and the way I was doing it it through a function that return text inside a loop, something like this:
function getLi(data) {
return '<li>' + data + '</li>';
}
then I found a better way to do it by writing html inside a div:
<div style="display:none;" id="Template">
<li id="value"></li>
</div>
and then I would change the id and value get the html and reset the element to original state:
var element = $("#value");
element.html(data);
element.attr('id', getNewId());
var htmlText = $("#Template").html();
element.html('');
element.attr('id', 'value');
return htmlText;
then I was reading on script template
and I figured this could be a better way of doing it,
However apply the previous code didn't work as the inner elements didn't exist according to this article
so how can I apply this?
EDIT:
I put inside a ul tag, I use this method to get the items dynamically
EDIT2:
<li>
<a href="#" >
<span>
<span>
some text
</span>
</span>
</li>
this isn't necessarily what I have but something along the way
Edit3:
my ul does not exist orgialy it's generated dynamically
I insist this is not a duplicate I want to know how to use a template with some dynamic variables
You could do the following way. It's clean, reusable and readable.
//A function that would return an item object
function buildItem(content, id) {
return $("<li/>", {
id: id,
html: content
});
}
In your loop, you could do the following. Do not append each LI inside the loop as DOM manipulation is costly. Hence, generate each item and stack up an object like below.
var $items = $();
// loop begin
var contents = ['<span><span>', data, '</span></span>'].join('');
var $item = buildItem(contents, getNewId());
$items.add($item);
// loop end
Just outside the loop. append those generated LIs to the desired UL, like below.
$("ul").append($items);
This is what I'd do and I am sure there are many better ways. Hope that helps.
One option is to upgrade to a modern JavaScript framework like AngularJS and then you could do it in one line using ng-repeat.
This would serve your purpose and make you more money as a developer.
If you're going to repeat this, use a templating system. Like {{ mustache }} or Handlebars.js.
If not, you can do this.
<ul>
<li class="hidden"></li>
</ul>
And in Javascript
$('ul .hidden').clone().removeClass('hidden').appendTo('ul');
And CSS, of course
.hidden { display:none }
Try this...
function getLi(data,ID) {
return $('<li id = "'+ ID + '">' + data + '</li>');
}
It returns javascript objest of Li..and you append it where ever you need.
what you need is using jquery templates, in the bellow link you can use good one which I'm using.
you create your template and prepare you JASON object of data.
after that every thing will be ready in one function call, more details in this link.
jquery.tmpl
hope this helps you and any one come to here in future..

Changing getElementById to getElementsByClassName

I am sure is a common problem in JS, i have created resizing function using JS, initially this function was only going to be used on one item of my site, however now i have chosen to use it on multiple items. My JS function currently uses document.getElementById(id); however i want to change this so it looks out for the class name .resize. How can this be done?
Below is a snippet of my JS
function ebi(id){
return document.getElementById(id);
}
if(ebi('resize')
.complete==true){
iscale(ebi(''));
}
ebi('resize')
.onload=function(){iscale(this);
}
function iscale(o){
//alert(o.width);
var sar=o.width/o.height;
var tar=1,tiw,tih,xoff,yoff;
if(o.width<=425&&o.height<=467){
tiw=o.width;
tih=o.height;
}else if(tar>sar){
tiw=425*sar;
tih=467;
}
else{
tiw=425;
tih=467/sar;
}
xoff=(680-tiw)>>1;
yoff=(209-tih)>>1;
//alert(xoff);
o.width=tiw;o.height=tih;
o.style.top=yoff+"px";
o.style.left=xoff+"px";
}
function $(id){return document.getElementById(id);}
html
<section id="homeSlide" class="shadow">
<img id="resize"class='opaque' src="http://www.colette.fr/media/push/pony_01239.jpg" />
<img id="resize" src="http://www.colette.fr/media/push/EGIFT_01234.jpg" />
<img id="resize" src="http://www.colette.fr/media/push/swa_mmm_001255.jpg" />
</section>
At the moment the resize function only works for the first image, however the other images do not resize. Before anyone attacks me about web standards the id's in the img tags were just to test the function.
id have to be unique, that's why this didn't works.
You can use document.getElementsByClassName() (as you thought) : simply add class="resize" (instead of id="resize") and use that kind of loop:
function callResize()
{
var elements = document.getElementsByClassName("resize");
for (var i=0;i<elements.length;i++)
{
var element = elements[i];
// do things with element
iscale(element);
}
}
document.getElementById('homeSlide').onload = function(){callResize();}
the point is id should be unique and to do what you want why don't you use jQuery. using jQuery you can easily do what you want, using this
jQuery("#homeSlide>img")
and you can also add this:
jQuery("#homeSlide>img").each(function(imgNode){
resize(imgNode);
});
but if you want to do it without jQuery, as far as some functions like getElementsByClassName and getElementsByTagName are not cross browser, you'd better try this:
var homeSlide = document.getElementById('homeSlide');
var myImgNodes = [];
for(var i=0;i<homeSlide.childNodes.length;i++){
var childNode = homeSlide.childNodes[i];
if(childNode.nodeType==1 && childNode.tagName.toLowerCase()=="img"){
myImgNodes.push(childNode);
}
}
afterwards myImgNodes would be exactly what you want.
I just changed your fiddle example and tried not to change your functionality, you can check this out, it perfectly works. This is your updated jsfiddle DEMO
First, an ID has to be unique in your HTML document. But in your example code you assign the ID "resize" three times. Just replace the ID from all three elements and add class="resize".
Second, I suggest to use the jQuery.each() method to iterate over the returned collection instead of a loop.
Example:
var elements = document.getElementsByClassName("resize")
jQuery.each(elements, function(element) {
iscale(element)
})

Problem using dynamically added html-object from javascript

I have a problem with dynamically including an object-tag in my html.
We have a external service which we call to get some html-fragment, it includes an object-tag, a script and a simple html-form. I take that content and add it to a div in my page and then try to execute the script that uses the included object. When i debug using Firebug I can see that the code is correctly inserted in the page but the script gets an error when it tries to access the object. It seems to me that the object isn’t initialized. Let me show you some code to exemplify what I mean.
getFragment makes an ajax call using jQuery to get the content.
var htmlSnippet = RequestModule.getFragment( dto );
$('#plugin').html( htmlSnippet ).hide();
The included content in plugin-div looks like this
<div id="plugin" style="display: none; ">
Browser:
Chrome
<object name="signer" id="signer" type="application/x-personal-signer2"></object>
<form method="POST" name="signerData" action="#success">
<input name="nonce" value="ASyhs..." type="hidden">
<input name="signature" value="" type="hidden">
<input name="encodedTbs" value="U2l..." type="hidden">
<input name="provider" value="nexus-personal_4X" type="hidden">
<input type="submit" onclick="doSign()" value="Sign">
</form>
</div>
The javascript that tries to use the “signer” object looks like this:
function doSign(){
var signer2 = document.getElementById("signer");
retVal = signer2.SetParam('TextToBeSigned', 'some value...');
... and then some more
}
It’s when i call the signer2.SetParam method that I get an error saying
Object #<an HTMLObjectElement> has no method 'SetParam'
But when I use the original page where the content is loaded when the page loads the script works so I know that the ‘SetParam’ method exists on the object and that the script works. But somehow it doesn’t work when I dynamically add it to the page afterwards.
I’ve Googled this a lot the last couple of days with no luck.
Does anyone have any idea on how to get this to work?
Best regards,
Henrik
First of all Object tag is not fully supported in all browsers (Source)
Next, from my experience, jQuery (which heavily relies on document.createDocumentFragment) sometimes fails to attach/trigger events on dynamically created/cloned DOM nodes, which could explain why your object failed to initialize.
That said, to try and fix your problem, I suggest using native document.createElement and document.appendChild methods instead of jQuery.html. You can try document.innerHTML but if that fails, you can always go with the ones I mentioned earlier.
My suggestion is to either alter your service to replace:
<script type="text/javascript">
function addElement(parentid, tag, attributes) {
var el = document.createElement(tag);
// Add attributes
if (typeof attributes != 'undefined') {
for (var a in attributes) {
el.setAttribute(a, attributes[a]);
}
}
// Append element to parent
document.getElementById(parentid).appendChild(el);
}
addElement('plugin', 'object', {name:"signer",id:"signer",type:"application/x-personal-signer2"});
</script>
OR if you cannot change the content that is returned by the service, run this after you include the content onto your page:
<script type="text/javascript">
/*
* Goes through al the object tags in the element with the containerid id
* and tries to re-create them using the DOM builtin methods
*/
function reattachObjectTags(containerid) {
jQuery('#'+containerid+' object').each(function(){
var attrs = {}, el = this;
// We're insterested in preserving all the attributes
var saved_attrs = {}, attr;
for(var i=0; i < el.attributes.length; i++) {
attr = el.attributes.item(i);
if(attr.specified) {
saved_attrs[attr.nodeName]=attr.nodeValue;
}
}
this.parentNode.removeChild(this);
var new_element = document.createElement('object');
for (var a in saved_attrs) {
new_element.setAttribute(a,saved_attrs[a]);
}
document.getElementById(containerid).appendChild(new_element);
});
}
// Do your stuff
var htmlSnippet = RequestModule.getFragment( dto );
$('#plugin').html( htmlSnippet ).hide();
// reattach all the object elements in #plugin
reattachObjectTags('plugin');
</script>
THIS IS ALL UNTESTED -
I typed this off the top of my mind, since I don't have the means to fire up IE and test this.
For a jQuery solution, I think this should work:
$("input:submit").click(function(){
$("#signer").append('<param name="TextToBeSigned" value="some value ...">');
... and then some more
});
Might want to give the submit button a class or an id and use that as a selector, if you have multiple forms on that page though.
Hope this helps.
I've set up a test script here: https://dl.dropbox.com/u/74874/test_scripts/object.html
If you open up Firebug/Web Inspector, you'll see that the SetParam method is in-fact, not defined. I don't know what it's supposed to do, but it's not defined in either case. If you're trying to add <param> tags to your embed, you could use the DOM API to do that. There is some code in the test script that does that, but I'll paste it here anyway:
var obj_signer = document.getElementById('signer');
var obj_p = document.createElement('param');
obj_p.id = "myp2";
obj_p.name = "TextToBeSigned";
obj_p.value = "some value ...";
obj_p.setAttribute('valueType', 'ref');
obj_signer.appendChild(e);
Or be faster using jQuery:
$("#signer").append("<param id='myp2' name='TextToBeSigned' value='some value ...' valueType='ref'></param>");

Categories

Resources