Accessing items in Javascript Node List to alter image - javascript

I am trying to create the same effect of CSS Hover but with Javascript code (for the purpose of learning Javascript and for future use). So on mouseover, I would like the individual image to increase opacity.
The code I have written below does not work. Please see comments for explantion regarding what I am trying to do;
<script>
//gets all img tags (qu.20)
var images = document.getElementsByTagName("img")
// Create new element with id "newNode" for the individual node to go into
var node = document.body.createElement("newNode")
// Add the new element into the html document
document.body.appendChild(newNode)
// Attach var i to the individual nodes and set src of new element as that node
function overImage () {
for (var i=0; i<images.length; i++) {
document.getElementById("newNode")
document.body.newNode.src = images.item(i)
}
}
// function to create a new class with same attributes as original when mouse leaves image
function leaveImage () {
for (var i=0; i<images.length; i++) {
document.getElementById("newNode")
document.body.newNode.src = images.item(i)
document.body.newNode.className = " leave"
}
}
</script>
<html>
<img src="image1.gif" onmouseover="overImage()" onmouseout="leaveImage()" alt="image" />
<img src="image2.gif" onmouseover="overImage()" onmouseout="leaveImage()" alt="image" />
</html>
<style>
img { opacity:0.5; }
#newNode { opacity:1; }
#newNode.leave { opacity:0.5; }
As an alternative, this code works but only on all images (ie. they all change opacity together, not individually.
<script>
function overImage () {
var selectImage = document.getElementsByTagName("img")
for (var i=0; i<selectImage.length; i++) {
selectImage[i].className = " over"
}
}
function leaveImage () {
var selectImage = document.getElementsByTagName("img")
for (var i=0; i<selectImage.length; i++) {
selectImage[i].className = ""
}
}
</script>
<style>
img { opacity:0.5; }
.over { opacity:1; }
</style>
Answers in Javascript only please with explanations. No jquery

You can do this in a much simpler manner, check this example:
var f = function(e) {
// the event target, can be any element in the page at this point
var t = e.target;
// check if the event target is an img element
if(t.tagName.toLowerCase() == 'img') {
// then toggle its active class
t.classList.toggle('active');
}
}
// add listeners to the window (or on whatever image container you have)
addEventListener('mouseover', f /* call this function f on mouseover */, false);
addEventListener('mouseout', f, false);
img { opacity: .5; }
.active { opacity: 1; }
<img src='http://i.imgur.com/kk7fJccs.jpg'/>
<img src='http://i.imgur.com/kk7fJccs.jpg'/>
<img src='http://i.imgur.com/kk7fJccs.jpg'/>
<img src='http://i.imgur.com/kk7fJccs.jpg'/>
<img src='http://i.imgur.com/kk7fJccs.jpg'/>
This code is going to work no matter how many images you add after this. It eliminates for you to add calls to your JS functions (whose names you may choose to change) to the HTML, the need for messing with the DOM from JS, the need for looping in the JS. And as far as the CSS is concerned, it's not using ids for styling, so it's avoiding specificity issues.

Your issue appears to be right at the top, document.body.createElement("newNode") will give you a TypeError: undefined is not a function. The createNode method is on the #document node, not a HTMLElement.
Next, you create nodes by tag name, there is no such tag <newNode>, maybe you meant to create an <img>
var node = document.createElement("img");
Now you need to assign the id attribute to it,
node.setAttribute('id', 'newNode');
Next, you have to append node to your document tree (you're currently trying to append an undefined variable newNode)
document.body.appendChild(node);
Finally, your two functions overImage and leaveImage have several problems of their own;
They are performing document.getElementById but not remembering the result, instead trying to go through the DOM tree in an unusual way and also you're trying to assign a node as a src, when you probably want to assign a String
// outside loop
var node = document.getElementById("newNode");
// inside loop
node.src = images.item(i).src;
They loop over all of images each time, meaning you will always finally end up with node's src pointing at the value from the last item in images
Try linking up these listeners using foo.addEventListner(type, event_handler) where foo is a reference to each node you want to attach the event_handler to. This will let you access the mouseover or mouseout event in more detail, especially if event_handler looks at it's first argument which will be the event itself, or this which will be the node which invoked the handler.
Always check your console as the first step in debugging, it'll usually let you quickly narrow down your issue to the exact line with the problem

Related

Adding javascript programmatically to dynamicly generated hyperlinks with the same id

Might be a strange setup, but I have a number of hyperlinks on the page with the same id (yeah, I know, but it was not my choice and I cannot change that at this time plus those hyperlinks are generated dynamically).
Example:
<div id="Links">
<div class="myItem">Some text</div>
<div class="myItem">More text</div>
<div class="myItem">Even more text</div>
</div>
Now I need to attach javascript to those links dynamically (the hyperlinks are also dynamically generated). The easiest way I see is by getting all hyperlinks on the page and then check the hyperlink id to ensure I only take care of those that have id of "myLink" (I have many other hyperlinks on the page).
I thought of using getElementById but that would only grab the first element with the specified id.
am attaching javascript to those links using the following:
window.onload = function() {
var anchors = document.getElementsByTagName('a');
for(var i = 0; i < anchors.length; i++) {
var anchor = anchors[i];
if (anchor.id='myLink')
{
if (anchor.getAttribute("LinkID") != null)
{
anchor.onclick = function() {
MyFunction(this.getAttribute("LinkID"), false);
return false;
}
}
}
}
}
The above function works fine, but it creates another issue - affects the styling of other hyperlinks on the page. So I was wondering if there is a way to accomplish the same thing but without affecting other elements on the page?
This is more modern and corrects your equality test:
window.onload = function() {
var anchors = document.getElementsByTagName('a');
for(var i = 0; i < anchors.length; i++) {
if (anchor[i].id==='myLink' && anchor[i].getAttribute("LinkID") !== null)
{
anchor[i].addEventListener("click", function() {
MyFunction(this.getAttribute("LinkID"), false);
}
}
}
}
Even with your original code, I don't see anything that would interfere with styling in the code. Can you elaborate as what styling changes you were getting?
You can use an attribute selector and document.querySelector([id=<id>]) pretty reliably depending on your browser support situation: http://codepen.io/anon/pen/YwLdKj
Then, of course, loop through that result and make subsequent changes or event bindings.
If not, you could use jQuery (referenced in above code pen).
You might also use JavaScript event delegation and listen for all click events, check if the user is clicking a link with the correct id.
If a combination of tag == 'a', class == "myItem" and presence of a LinkID attribute is sufficient to identify nodes requiring a click handler they could be identified using multiple CSS selectors. If this is not possible however, a query selector not using id can create a list of nodes to be checked for id, as for example:
function callMyFunction()
{ MyFunction(this.getAttribute("LinkID"), false);
}
function addClickHandlers()
{ var list = document.querySelectorAll("a[LinkID]")
var i, node;
for( i = 0; i < list.length; ++i)
{ node = list[i];
if(node.id == "myLink")
{ node.onclick=callMyFunction;
}
}
}
See also running a selector query on descendant elements of given node if of interest.

How do I repeat div classes using JavaScript only?

Okay, I'm unsure how to word the question, but basically I want to repeat my div containers that have a class of "blocks" using only javascript, no HTML (other than the HTML needed to start a page). IF I were doing this using HTML the result should look exactly like this.
http://jsfiddle.net/nqZjB/1/
<div class = "blocks"> <!-- Repeats three times -->
However as I stated in the description I do not want to use any HTML, so here is my fiddle with javascript only.
How do I make div class blocks repeat three times as in my HTML example using only javascript? Of course in real life I would use HTML for this as javascript is unnecessary, but I want to do this in pure javascript so I can learn. Also as a sidenote if you have a better way as to how I should have worded the question, let me know.
Thanks (:
http://jsfiddle.net/TbCYH/1/
It's good you see the use of making a function of a re-occurring pattern.
Before posting it in StackOverflow, have you tried doing it yourself?
jsfiddle: http://jsfiddle.net/kychan/W7Jxu/
// we will use a container to place our blocks.
// fetch the element by id and store it in a variable.
var container = document.getElementById('container');
function block(mClass, html) {
//extra html you want to store.
return '<div class="' + mClass + '">' + html + '</div>';
}
// code that loops and makes the blocks.
// first part: creates var i
// second: condition, if 'i' is still smaller than three, then loop.
// third part: increment i by 1;
for (var i = 0; i < 3; i++) {
// append the result of function 'block()' to the innerHTML
// of the container.
container.innerHTML += block('block', 'data');
}
Edit: JS has changed a lot since the original post. If you do not require compatibility, use const, template literals, class and querySelector to make the code a bit cleaner. The following code has a Builder class and assumes there is a div with ID 'container':
// create class builder.
class Builder {
// create constructor, accept an element selector, i.e #container.
constructor(targetContainerSelector) {
// search element by given selector and store it as a property.
this.targetContainer = document.querySelector(targetContainerSelector);
}
// method to append to innerHtml of target container.
appendUsingInnerHtml(divAsHtml) {
this.targetContainer.innerHTML += divAsHtml;
}
// method to append to target container using DOM elements.
appendUsingDom(divAsDom) {
this.targetContainer.appendChild(divAsDom);
}
}
// constant to hold element selector.
const myTargetContainer = '#container';
// constant to set the class if required.
const myDivClass = 'my-class';
// constant to hold the instantiated Builder object.
const builder = new Builder(myTargetContainer);
// loop 3 times.
for (let i=0; i<3; i++) {
// call method to append to target container using innerHtml.
builder.appendUsingInnerHtml(`<div class="${myDivClass}}">innerhtml div text</div>`);
// OR.. build using DOM objects.
// create the div element.
const div = document.createElement('div');
// create text element, add some text to it and append it to created div.
div.appendChild(document.createTextNode('dom div text'));
// call method to append div DOM object to target container.
builder.appendUsingDom(div);
}
Please note: Every time something is added to the DOM, it forces the browser to reflow the DOM (computation of element's position and geometry).
Adding everything at once, improve speed, efficiency and performance of a code.
(ref: document.createDocumentFragment)
window.onload = Create();
function Create() {
// create the container
var mainContainer = document.createElement('div');
mainContainer.id = 'mainContainer';
// add all style in one go
mainContainer.setAttribute('style', 'witdht: 400px; height: 200px; border: 2px solid green; margin-left: 20px;');
var divBlocks1 = document.createElement('div');
divBlocks1.className = 'blocks';
divBlocks1.setAttribute('style', 'width: 100px; heigth: 100px; border: 1px solid black; margin-left: 20px; margin-top: 10px; floar: left;');
var divBlocks2 = divBlocks1.cloneNode(false); // copy/clone above div
var divBlocks3 = divBlocks1.cloneNode(false); // copy/clone above div
// everything is still in memory
mainContainer.appendChild(divBlocks1);
mainContainer.appendChild(divBlocks2);
mainContainer.appendChild(divBlocks3);
// now we append everything to the document
document.body.appendChild(mainContainer);
}
Good luck
:)
for(var d=0;d<10;d++){
var aDiv = document.createElement('div');
aDiv.className = "block";
document.getElementsByTagName('body')[0].appendChild(aDiv);
}
Rather than creating the elements before hand and then appending them to the main container, consider dynamically creating and appending them in a loop.
http://jsfiddle.net/TbCYH/6/
for(var i = 0; i < 3; i++) {
var divBlock = document.createElement("div");
divBlock.className = "blocks";
mainContainer.appendChild(divBlock);
}
In the above code snippet a div is being created and appended for each iteration of the loop (which is set to cease at 3).
Also if possible, always use CSS classes rather than modifying the styles for each div directly.

adding inline styling on appended items on run time having issues

I have an "a" tag into a "li" and am appending few images on hover of a "li" into same "a" tag and I have to use inline style on all those img elements but the problem is when I hover first time on "li" these styles apply only on first img tag which is always exist there but not on others but if I hover over "li" again then those inline styles applies on all img tags. for this am using this JS code given below:
$(document).ready(function() {
var mouseover_interval;
var $image;
$('li.product-details').mouseenter(function() {
current_image = -1;
$image = $(this).find('a.current_product_image img');
data_srcs = $image.attr('data-srcs').split(",");
if(data_srcs.length >1){
for (var i = data_srcs.length - 1; i >= 0; i--) {
img = new Image ;
img.src = data_srcs[i];
new_img = $('<img>').attr('src', data_srcs[i]).css({display:"none"}).addClass("over");
$(this).find('a.current_product_image').append(new_img);
var countImg = $(this).find('a.current_product_image img');
countImg.each(function(){
$(this).css({
position : 'absolute',
left : '50%',
marginLeft : -$(this).width()/2,
top : '50%',
marginTop : -$(this).height()/2,
});
});
}
}
else{
return false;
}
$images = $(this).find('a.current_product_image img.over');
mouseover_interval = setInterval(function(){
{
$image.fadeOut(500);
if(current_image == -1){
$($images[$images.length-1]).fadeOut(500);
}
else{
$($images[current_image]).fadeOut(500);
}
current_image+=1;
$($images[current_image]).fadeIn(500);
if(current_image == $images.length-1){
current_image = -1;
}
}
}, 1000);
}).mouseleave( function(){
clearInterval(mouseover_interval);
$image.fadeIn(500);
$(this).find('a.current_product_image img.over').remove();
});
});
How to add styles on all appended elements hovering over "li" first time? Please let me know if am using anything wrong there.
Thanks in advance,
Ashwani Sharma
The reason this is happening is - most likely - the additional images haven't loaded into the DOM yet (remember that it takes time for assets - particularly images - to load when you add them in dynamically).
To confirm this, try logging the countImg var and see whether it reports one too few for the number of images you expect to have. I suspect that's your issue.
You could try passing attributes into the element before adding it into the page. Something like this:
new_img = $('<img>', {
src: data_srcs[i],
class: 'over'
}).hide();
This should create an object that looks like:
<img src="your/source.jpg" class="over" style="display: none;" />
Your problem will still be that it won't actually load into the page until you turn off the display: none, most browsers are intelligent enough to not pull images until they are actually needed (ie: not when hidden).
Also note that your declaration of $image is only set once, at the very beginning of the function. It therefore will only contain the elements it finds at that point in time. If you dynamically add additional images (ie: your new_img)to the parent element, they won't automatically get added to that $image variable.

replace specific tag name javascript

I want to know if we can change tag name in a tag rather than its content. i have this content
< wns id="93" onclick="wish(id)">...< /wns>
in wish function i want to change it to
< lmn id="93" onclick="wish(id)">...< /lmn>
i tried this way
document.getElementById("99").innerHTML =document.getElementById("99").replace(/wns/g,"lmn")
but it doesnot work.
plz note that i just want to alter that specific tag with specific id rather than every wns tag..
Thank you.
You can't change the tag name of an existing DOM element; instead, you have to create a replacement and then insert it where the element was.
The basics of this are to move the child nodes into the replacement and similarly to copy the attributes. So for instance:
var wns = document.getElementById("93");
var lmn = document.createElement("lmn");
var index;
// Copy the children
while (wns.firstChild) {
lmn.appendChild(wns.firstChild); // *Moves* the child
}
// Copy the attributes
for (index = wns.attributes.length - 1; index >= 0; --index) {
lmn.attributes.setNamedItem(wns.attributes[index].cloneNode());
}
// Replace it
wns.parentNode.replaceChild(lmn, wns);
Live Example: (I used div and p rather than wns and lmn, and styled them via a stylesheet with borders so you can see the change)
document.getElementById("theSpan").addEventListener("click", function() {
alert("Span clicked");
}, false);
document.getElementById("theButton").addEventListener("click", function() {
var wns = document.getElementById("target");
var lmn = document.createElement("p");
var index;
// Copy the children
while (wns.firstChild) {
lmn.appendChild(wns.firstChild); // *Moves* the child
}
// Copy the attributes
for (index = wns.attributes.length - 1; index >= 0; --index) {
lmn.attributes.setNamedItem(wns.attributes[index].cloneNode());
}
// Insert it
wns.parentNode.replaceChild(lmn, wns);
}, false);
div {
border: 1px solid green;
}
p {
border: 1px solid blue;
}
<div id="target" foo="bar" onclick="alert('hi there')">
Content before
<span id="theSpan">span in the middle</span>
Content after
</div>
<input type="button" id="theButton" value="Click Me">
See this gist for a reusable function.
Side note: I would avoid using id values that are all digits. Although they're valid in HTML (as of HTML5), they're invalid in CSS and thus you can't style those elements, or use libraries like jQuery that use CSS selectors to interact with them.
var element = document.getElementById("93");
element.outerHTML = element.outerHTML.replace(/wns/g,"lmn");
FIDDLE
There are several problems with your code:
HTML element IDs must start with an alphabetic character.
document.getElementById("99").replace(/wns/g,"lmn") is effectively running a replace command on an element. Replace is a string method so this causes an error.
You're trying to assign this result to document.getElementById("99").innerHTML, which is the HTML inside the element (the tags, attributes and all are part of the outerHTML).
You can't change an element's tagname dynamically, since it fundamentally changes it's nature. Imagine changing a textarea to a select… There are so many attributes that are exclusive to one, illegal in the other: the system cannot work!
What you can do though, is create a new element, and give it all the properties of the old element, then replace it:
<wns id="e93" onclick="wish(id)">
...
</wns>
Using the following script:
// Grab the original element
var original = document.getElementById('e93');
// Create a replacement tag of the desired type
var replacement = document.createElement('lmn');
// Grab all of the original's attributes, and pass them to the replacement
for(var i = 0, l = original.attributes.length; i < l; ++i){
var nodeName = original.attributes.item(i).nodeName;
var nodeValue = original.attributes.item(i).nodeValue;
replacement.setAttribute(nodeName, nodeValue);
}
// Persist contents
replacement.innerHTML = original.innerHTML;
// Switch!
original.parentNode.replaceChild(replacement, original);
Demo here: http://jsfiddle.net/barney/kDjuf/
You can replace the whole tag using jQuery
var element = $('#99');
element.replaceWith($(`<lmn id="${element.attr('id')}">${element.html()}</lmn>`));
[...document.querySelectorAll('.example')].forEach(div => {
div.outerHTML =
div.outerHTML
.replace(/<div/g, '<span')
.replace(/<\/div>/g, '</span>')
})
<div class="example">Hello,</div>
<div class="example">world!</div>
You can achieve this by using JavaScript or jQuery.
We can delete the DOM Element(tag in this case) and recreate using .html or .append menthods in jQuery.
$("#div-name").html("<mytag>Content here</mytag>");
OR
$("<mytag>Content here</mytag>").appendTo("#div-name");

How to append an element, all its children, and all classes of the parent and children with jQuery

I have a function that is successful in removing an element and appending it elsewhere on the page as successful. The problem is that as soon as the document is ready jQuery adds classes and attributes to the children that upon moving are lost. I need these classes and attributes to remain after removing and appending. I have thought about calling the original function that adds the classes, but the problem is they are key based and rely on their position prior to the move, calling it after changes the key and thus will add brand new and different classes.
The classes adding jQuery is pretty standard:
$(function(){
$("div").each(function(key){
if ($(this).hasClass("container")){
$(this).find("ul").addClass("parent" + key);
$(this).find(".container-item").attr("parentClass", ".parent" + key);
};
});
});
The remove/append function:
function copy_item(draggable, target){
var account = clone_items(draggable);
//$('#'+name.uid).remove();
$('#'+name.uid).hide();
target.append(make_div(name, true, true));
//$(draggable).children().attr("class", ($(draggable).children().attr("class")));
}
function make_div(name, drag, drop){
var newdiv = document.createElement('div');
newdiv.setAttribute('id', name.uid);
newdiv.appendChild(make_h3(name.username));
ul = document.createElement('ul');
ul.setAttribute("class", "domain_list");
newdiv.appendChild(ul);
for (j = 0; j < name.domains.length; ++j) {
ul.appendChild(make_li(name.domains[j], drag));
}
return newdiv;
}
The end result in the HTMl is basically:
<div class="container">
<ul class="parent0">
<li parentClass="parent0">
<li parentClass="parent0">
When recreating this structure, I need to have the class "parent0" and the parentClass attribute intact. Above you can see I've tried hiding the element, ensuring that it still stays a valid element with the correct classes/attributes, but in the end that still didn't work out. Ideally, I could remove the element entirely and recreate it with the correct classes.
If I am correct in my understanding of what you are trying to do, you do not need to .remove() and recreate the element in order to move it. You can just do this:
function copy_item(draggable, target) {
// not sure what this variable is for
// as you don't seem to be using it?
var account = clone_items(draggable);
// ...however, appending an existing
// element to another will 'move' it
// and preserve all of it's properties
target.append($('#' + name.uid));
}

Categories

Resources