I have a data collection thats gets rendered from the server. This is how the HTML might looks like:
<div>
<img class="image_98" alt="..." height="..." src="..." width="..." />
</div>
<div>
<img class="image_99" alt="..." height="..." src="..." width="..." />
</div>
<div>
<img class="image_99" alt="..." height="..." src="..." width="..." />
</div>
<div>
<img class="image_100" alt="..." height="..." src="..." width="..." />
</div>
<div>
<img class="image_100" alt="..." height="..." src="..." width="..." />
</div>
<div>
<img class="image_100" alt="..." height="..." src="..." width="..." />
</div>
The image class is dynamic depending on the id of the image in the database. How can I hide all images except the first one of every class?
In this example I only want to see the first "image_98", "image_99" and "image_100" class.
You can do it like this, using attribute selector [attribute="value"] and each() method
var img=$('[class^="image_"]');
img.hide(); //hide all image
img.each(function(){
$('.'+this.className).eq(0).show(); // show first image in each class
});
Use
show() , to show the selected item
hide() , to hide the selected item.
Old school method :
var slice = Array.prototype.slice,
imgs = slice.call(document.getElementsByTagName('img')),
img, ct, cls;
while (img = imgs.shift()) {
if (cls === img.className) {
ct = img.parentNode;
ct.parentNode.removeChild(ct);
} else {
cls = img.className;
}
}
Yuk indeed...
Using jQuery, I would do quite the same :
var prevCls;
$('img').each(function () {
var cls = $(this).attr('class'); // I have a doubt about .className support
if (prevCls === cls) {
$(this.parentNode).remove(); // or .hide() if preferred
} else {
prevCls = cls;
}
});
Advantage comparing to the accepted answer : a single loop.
Here is another method using recursivity - quite close to the first one :
(function deldup(list, prevCls) {
var item = list.shift();
if (item.className !== prevCls) {
prevCls = item.className;
} else {
item = item.parentNode;
item.parentNode.removeChild(item);
}
if (list.length) {
deldup(list, prevCls);
}
})(
Array.prototype.slice.call(
document.getElementsByTagName('img')
)
);
Advantage : no global variables.
In case images are mixed :
(function deldup(list, cls) {
var item = list.shift();
if (!cls[item.className]) {
cls[item.className] = true;
} else {
item = item.parentNode;
item.parentNode.removeChild(item);
}
if (list.length) {
deldup(list, cls);
}
})(
Array.prototype.slice.call(
document.getElementsByTagName('span')
), {} // notice this empty object
);
Demo : http://jsfiddle.net/wared/QpZD5/.
Related
I'm working on a system that puts all images inside paragraph-tags, as such:
<p>
<img src="..." />
</p>
So I wrote a function to move the images out of the paragraph tags. It looks like this:
moveImagesOutOfPtags() {
let images = document.querySelectorAll('p > img');
Object.entries(images).forEach(entry => {
let value = entry[1];
if( value.parentNode.nodeName === 'P' || value.parentNode.nodeName === 'p' ){
value.parentNode.outerHTML = value.parentNode.innerHTML;
}
});
}
But it doesn't work if there are two images inside the same p-tag, as such:
<p>
<img src="..." />
<img src="..." />
</p>
... Since the parent is removed/rewritten/overwritten with the value.parentNode.outerHTML = ...-line, for the first image. So when it gets to the second image in that p-tag, then it throws the error:
Failed to set the 'outerHTML' property on 'Element': This element has no parent node.
Any suggestions on a good way to solve this?
This is easy to achieve with DOM manipulating methods instead of setting HTML strings, like this:
function moveImagesOutOfPtags() {
let images = document.querySelectorAll('p > img');
images.forEach(img => {
const parent = img.parentElement;
// Insert the image as a previous sibling to the paragraph
parent.parentElement.insertBefore(img, parent);
if (parent.children.length === 0) {
// Remove the empty paragraph
parent.remove();
}
});
}
moveImagesOutOfPtags();
<p>
<img src="...">
<img src="...">
</p>
<p>
<img src="...">
</p>
You don't have to check the parent, since the query will select the images wrapped in a paragraph only, just insert the image as a previous sibling of the parent. Then check if the parent is empty, and remove if needed.
use id for every paragraph-tags and check the previously modified id with current one . i don't think this is the best way but this will let the job done.
html contents
<p id = "p_id_1">
<img src="..." />
<img src="..." />
</p>
<p id = "p_id_2">
<img src="..." />
<img src="..." />
</p>
script will be
moveImagesOutOfPtags() {
let images = document.querySelectorAll('p > img');
var p_id='';
Object.entries(images).forEach(entry => {
let value = entry[1];
if(p_id != value.parentNode.id && (value.parentNode.nodeName === 'P' || value.parentNode.nodeName === 'p' )){
value.parentNode.outerHTML = value.parentNode.innerHTML;
p_id = value.parentNode.id;
}
});
}
You could just catch the error and not carry on like this:
Try this:
moveImagesOutOfPtags() {
document.querySelectorAll('p > img').forEach(entry => {
const value = entry && entry[0]; // catches error if p has been replaced and doesn't continue.
if(value){ // you don't have to test if parent is p tag, you've already determined that with your direct child selector p > img
value.parentNode.outerHTML = value.parentNode.innerHTML;
}
});
}
I'm currently trying to use JavaScript to produce a page with multiple image files that I want to change individually and separately when I click on them. The best way I can to describe it is if you can imagine a set of playing cards face down, and I want to be able to click each one and have it change to a different image, and to be able to click again to change back.
Please see JavaScript and code below that I have done so far. The script works on my first image, but not on any further images.
HTML:
<img id="imgOne" onclick="changeImageOne()" src="click_here.png" height="100" width="100">
<img id="imgTwo" onlick="changeImageTwo()" src="click_here.png" height="100" width="100">
JavaScript:
var changeImageOne = function() {
var imageOne = document.getElementById('imgOne');
if (imageOne.src.match("i.jpg")) {
imageOne.src = "click_here.png";
} else {
imageOne.src = "i.jpg";
}
}
var changeImageTwo = function() {
var imageTwo = document.getElementById('imgTwo');
if (imageTwo.src.match("l.jpg")) {
imageTwo.src = "click_here.png";
} else {
imageTwo.src = "l.jpg";
}
}
typo # onlick, it is onclick
var changeImageOne = function() {
var imageOne = document.getElementById('imgOne');
if (imageOne.src.match("i.jpg")) {
imageOne.src = "click_here.png";
} else {
imageOne.src = "i.jpg";
}
alert(imageOne.src);
}
var changeImageTwo = function() {
var imageTwo = document.getElementById('imgTwo');
if (imageTwo.src.match("l.jpg")) {
imageTwo.src = "click_here.png";
} else {
imageTwo.src = "l.jpg";
}
alert(imageTwo.src);
}
<img id="imgOne" onclick="changeImageOne()" src="click_here.png" height="100" width="100">
<img id="imgTwo" onclick="changeImageTwo()" src="click_here.png" height="100" width="100">
JavaScript :
<script>
function SWAPImage(idv,oldimg,newimg){
document.getElementById(idv).attributes.src.value=newimg;
document.getElementById(idv).attributes["alt-src"].value = oldimg;
}
</script>
HTML :
<img class="image" src="a.jpg" alt-src="a_other.jpg" id="img1" onClick="SWAPImage(this.id,this.attributes.src.value,this.attributes['alt-src'].value)" />
<img class="image" src="b.jpg" alt-src="b_other.jpg" id="img2" onClick="SWAPImage(this.id,this.attributes.src.value,this.attributes['alt-src'].value)" />
<img class="image" src="c.jpg" alt-src="c_other.jpg" id="img3" onClick="SWAPImage(this.id,this.attributes.src.value,this.attributes['alt-src'].value)" />
I used alt-src attribute to store other image on it.When click on any image.It swap src with alt-src.
I try to find out the number of <img> elements that do not have "style" attribute in a HTML file by using JavaScript.
My solution: find out numbers of <img> tags as "imgCount", then get number of <img> tags with "style" attribute as "imgStyCount". After that, use "imgCount" minus "imgStyCount" to get the final result that I wish to know.
However, something goes wrong. My browser keep told me
TypeError: document.getElementsByTagName(...)[K].hasAttribute is not a function
At the if statement.
And the weird thing is, the alert(document.getElementsByTagName("img")[k].hasAttribute("style") show the if statement result is TRUE.
How it can be like not a function and give the true value?
var imgCount = 0;
var imgStyCount = 0;
var result;
for (k in document.getElementsByTagName("img")) {
if (document.getElementsByTagName("img")[k].hasAttribute("style") == true) {
alert(document.getElementsByTagName("img")[k].hasAttribute("style"));
console.log(" <img> =: ", document.getElementsByTagName("img")[k].style);
imgStyCount++;
}
imgCount++;
}
result = imgCount - imgStyCount;
<img height="150px" src="Http://flax.nzdl.org/images/ngf.jpeg" style="vertical-align:middle;margin-right:20px;" />
<img src="Http://flax.nzdl.org/images/abc.jpg" />
<img src="Http://flax.nzdl.org/images/fbc.jpg" />
<img src="Http://flax.nzdl.org/images/agc.jpg" />
<img src="Http://flax.nzdl.org/images/abt.jpg" />
Here's a simple way without using loop.
You can use querySelectorAll with attribute selector
document.querySelectorAll('img[style]') will select all the <img> elements on the page having style attribute.
var result = document.querySelectorAll('img').length - document.querySelectorAll('img[style]').length;
alert(result);
<img height="150px" src="Http://flax.nzdl.org/images/ngf.jpeg" style="vertical-align:middle;margin-right:20px;" />
<img src="Http://flax.nzdl.org/images/abc.jpg" />
<img src="Http://flax.nzdl.org/images/fbc.jpg" />
<img src="Http://flax.nzdl.org/images/agc.jpg" />
<img src="Http://flax.nzdl.org/images/abt.jpg" />
Use for-loop to iterate image elements than for-in
var imgStyCount = 0;
var elems = document.getElementsByTagName("img");
for (var k = 0; k < elems.length; k++) {
if (elems[k].hasAttribute("style")) {
imgStyCount++;
}
}
var result = elems.length - imgStyCount;
alert(result);
<img height="150px" src="Http://flax.nzdl.org/images/ngf.jpeg" style="vertical-align:middle;margin-right:20px;" />
<img src="Http://flax.nzdl.org/images/abc.jpg" />
<img src="Http://flax.nzdl.org/images/fbc.jpg" />
<img src="Http://flax.nzdl.org/images/agc.jpg" />
<img src="Http://flax.nzdl.org/images/abt.jpg" />
Fiddle demo
You're welcome.
function hasAttr(el, attr) {
if(typeof el === 'object' && el !== null && 'getAttribute' in el && el.hasAttribute(attr)) return true
else return false
}
<span medium-img>Whatever</span>
alert($('span').is('[medium-img]')); // Returns true
Suppose my DOM looks something like this (purely fictional example):
<div>
<img id="img1" />
Text 1
</div>
<img id="img2" />
<div>
<p>Text 2</p>
<div>
<img id="img3" />
</div>
<img id="img4" />
</div>
What I'm attempting to do is to find all text (if any) between consecutive elements independent of nesting level (so in this case, I'd find Text1 between #img1 and #img2, Text 2 between #img2 and #img3, and nothing/an empty string between #img3 and #img4)
I don't know ahead of time how the dom is going to be structured.
I've tried using JQuery's nextUntil(), but that only seems to work for sibling nodes.
You can use the contents() method which returns all child nodes including textnodes.
This way, doing $('body *').contents().addBack() returns a flattened representation of the DOM.
Now, you can iterate between img (or whatever tag you want) elements and get the textnodes (having a nodeType of 3)
function textBetweenTags(tag){
var contents = $('body *').contents().addBack(),
allOfType = contents.filter(tag),
count = allOfType.length,
map = allOfType.map(function(){return contents.index( this );}),
texts = [];
for (var i = 0, l = map.length-1; i < l; i++){
var start = map[i],
end = map[i+1],
textnodes = contents.slice(start,end).get().filter(function(item,index){return item.nodeType===3;});
texts.push( {
start: contents[start],
end: contents[end],
text: $.trim($(textnodes).text())
});
}
return texts.filter(function(item,index){return item.text !== '';});
}
var texts = textBetweenTags('img');
console.log(texts);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
<img id="img1" />
Text 1
</div>
<img id="img2" />
<div>
<p>Text 2</p>
<div>
<img id="img3" />
</div>
<img id="img4" />
</div>
I think my solution is more closely achieves what you specified. This is also using contents() but does the traversing/filtering too, based on the ids.
function textBetweenIds(firstId, lastId) {
var texts = [],
betweenIds = false,
recurse = function ($item) {
$item.each(function() {
var text,
$item = $(this),
itemId = $item.attr("id"),
contents = $item.contents();
if (itemId == firstId) {
betweenIds = true;
} else if (itemId == lastId) {
betweenIds = false;
}
if (contents.length == 0 && betweenIds) {
text = $item.text().trim();
if (text != undefined && text.length > 0) {
texts.push(text);
}
}
recurse(contents);
});
return texts;
};
recurse($("body"));
return texts;
}
result = textBetweenIds("img1", "img3");
// result: ["Text 1", "Text 2"]
Say I had the following three divs with unique ids on a page
<div id="product-id-001"></div>
<div id="product-id-002"></div>
<div id="product-id-003"></div>
What code would I need to add the following image elements based on the id of the div?
<div id="product-id-001">
<img src="images/product-id-001-1.jpg"></img>
<img src="images/product-id-001-2.jpg"></img>
<img src="images/product-id-001-3.jpg"></img>
</div>
<div id="product-id-002">
<img src="images/product-id-002-1.jpg"></img>
<img src="images/product-id-002-2.jpg"></img>
<img src="images/product-id-002-3.jpg"></img>
</div>
<div id="product-id-003">
<img src="images/product-id-003-1.jpg"></img>
<img src="images/product-id-003-2.jpg"></img>
<img src="images/product-id-003-3.jpg"></img>
</div>
Thanks for any tips.
$('div[id^=product]').each(function(index, elem) {
for(var i = 1; i < 4; i++) {
$('<img>', {
src: '/images/' + elem.id + i
}).appendTo(this);
}
});
Demo: http://www.jsfiddle.net/9S9Av/
(You need Firebug or another DOM inspector to see the result)
From the jQuery docs:
"The .append() method inserts the specified content as the last child of each element in the jQuery collection"
So:
$('#product-id-001").append('<img src="images/product-id-001-1.jpg"></img>');
etc...
// Select all elements with an id beginning with product-id:
var $productContainers = $('[id^=product-id]');
// Loop through the matched elements and append the images:
$productContainers.each(function () {
var $this = $(this),
productId = $this.attr('id'),
numImages = 3,
extension = '.jpg';
// Create and append the images based on the configuration above. Note that this
// assumes that each product have three valid images.
for (var i = 1; i <= numImages; i++)
{
var $image = $('<img alt="" />').attr('src', 'images/'+productId+'-'+i+extension);
$image.appendTo($this);
}
});