.tagName property for immediate elements? - javascript

Does the .tagName property work for immediate elements? I have been getting it to work using child elements as a jump-off point, but I can't get it to work on immediate elements.
Here is an example that works:
HTML
<p>This is paragraph 1.</p>
<p>This is paragraph 2.</p>
<p>This is paragraph 3.</p>
<p>This is paragraph 4.</p>
<div>
<p id="foo">This is paragraph 5.</p>
</div>
JavaScript
(function(){
var el = document.getElementById("foo");
var name = el.parentNode.tagName;
alert(name);
}());
This gets me the name of the div element and this is what I want to achieve, but is there a shorter way?
I don't want to add an identifier to the element and target it that way, this is because I don't want to add unnecessary mark-up to my HTML document. Here is an example that works, but relies on an identifier.
HTML
<p>This is paragraph 1.</p>
<p>This is paragraph 2.</p>
<p>This is paragraph 3.</p>
<p>This is paragraph 4.</p>
<div id="bar">
<p id="foo">This is paragraph 5.</p>
</div>
JavaScript
(function(){
var el = document.getElementById("bar");
var name = el.tagName;
alert(name);
}());
As you see this get's the desired effect, but relies on an identifier. So I thought: "Can't I use the .tagName property on the element itself and target the element with .getElementsByTagName()?"
HTML
<p>This is paragraph 1.</p>
<p>This is paragraph 2.</p>
<p>This is paragraph 3.</p>
<p>This is paragraph 4.</p>
<div>
<p id="foo">This is paragraph 5.</p>
</div>
JavaScript
(function(){
var el = document.getElementsByTagName("div");
var name = el.tagName;
alert(name);
}());
The answer is no. This get's me a value of undefined. Why is this? Anyone have a solution?
Link to jsFiddle: http://jsfiddle.net/BV5EP/

Keep in mind that getElementsbyTagName (notice the plural s) returns a nodeList instead of an Element (like getElementById does). You need to make sure to use el[0].tagName!
Corrected Fiddle

This get's me a value of undefined. Why is this?
Because the NodeList that getElementsByTagName returns has no tagname property.
Anyone have a solution?
You have to access the elements that the NodeList contains by index – el[0].tagName in this case, if you only want to access the first (or only) div element.

Related

Remove <p> tags with only <br> in them javascript

I am trying to use javascript to remove HTML elements <p><br></p>
const blog = document.getElementById('blog');
const postNode = document.createElement('div');
postNode.innerHTML = postNode.innerHTML.replace('<p><br></p>', '');
blog.appendChild(postNode);
However, the replace doesn't seem to get rid of the strings within the innerHTML. I think it is because the output is currently
<h2>This is a heading two</h2><p>This is a paragraph</p><p><br></p><p>This is a paragraph with a <strong>bold</strong> an <em>italic</em> and an <u>underscored</u> text</p><p><br></p>
The problem <p><br></p>HTML is connected to other elements and thus not a string or substring in it's own right.
Is there some regex magic that can help me out?
Iterate through <p> tags, and filter them by whether the tag has exactly one child, and that child is a <br>. Then, .remove() each of the <p>s which pass that test:
[...postNode.querySelectorAll('p')]
.filter(p => p.children.length === 1 && p.children[0].tagName === 'BR')
.forEach(p => p.remove());
console.log(postNode.innerHTML);
<div id="postNode">
<h2>This is a heading two</h2>
<p>This is a paragraph</p>
<p><br></p>
<p>This is a paragraph with a <strong>bold</strong> an <em>italic</em> and an <u>underscored</u> text</p>
<p><br></p>
</div>
I think instead you can use a class to hide the elements you want to hide with
display: none;

Targeting all last (but not last-most) elements of a given tag

I have an HTML structure with three structures of some element and two p's right under:
<some_tag></some_tag>
<p></p>
<p></p>
<some_tag></some_tag>
<p></p>
<p></p>
<some_tag></some_tag>
<p></p>
<p></p>
In practice I have much more than just three structures (more like thirty) so I need automation.
I need to select each last p in the first two structures, but not the last p in the last structure.
My problem
There seem to be no CSS way to do the selection I desire.
Considering an HTML way of wrapping each non-last, some_tag structure in some other element (like a div) I conclude I don't like this solution as it seems to me unaesthetic.
My question
Do you know a way to automate the described selection via JavaScript?
This is not a good HTML structure for what you want to do. It will be much easier if you wrap each set of hX and p inside a container tag like a div.
Otherwise you are going to have to loop over each element with JavaScript and decide whether to apply styles (if the current element is a p, and there is a next element, and it's not a p).
You can do this with JavaScript, here is an example:
const elements = document.querySelectorAll("*");
let latestParagraph;
const paragraphsToStyle = [];
for(const element of elements){
if(element.tagName === "P"){
latestParagraph = element;
}else{
if(latestParagraph){
paragraphsToStyle.push(latestParagraph);
}
}
}
paragraphsToStyle.pop();
for(const paragraph of paragraphsToStyle){
paragraph.style.color = "red";
}
<h3>h</h2>
<p>p1</p>
<p>p2</p>
<h2>h</h2>
<p>p3</p>
<p>p4</p>
<h1>h</h2>
<p>p5</p>
<p>p6</p>
This will only work if there is no nesting.
The script runs through all the elements on the page and when a non paragraph element is encountered, it adds the latest paragraph found to the paragraphsToStyle array. At the end of the first loop, the last paragraph on the page is removed from the array paragraphsToStyle.pop()
Is your layout created dynamically? You could always use css classes to handle this easily. If you are not able to control this with layout here is a quick way to do it:
#myLayout *:not(:last-child) > p:last-child {
color: red
}
This code grabs every parent with a p child. It should ignore the last child sibling and always grab the last p. I would recommend replacing the * with a node or a css selector though to prevent unexpected results. I also recommend at least limiting possible issues by enclosing everything in a container like below.
<div id="myLayout">
<div>
<h2>h1</h2>
<p>p1</p>
<p>p2</p>
</div>
<div>
<h2>h1</h2>
<p>p1</p>
<p>p2</p>
</div>
<div>
<h2>h1</h2>
<p>p1</p>
<p>p2</p>
</div>
</div>
Edit: As others have mentioned, you may want to restructure your HTML so that it can work with selectors more easily. But if that's not possible, you can use JavaScript for this. In this case, all you have to do is see if the element is a 'p' tag and if the next element is not a 'p' tag:
var children = document.getElementById('content').children;
for (let i = 0; i < children.length - 1; i++) {
let nextTagName = children[i+1].tagName;
if (children[i].tagName === 'P' && nextTagName !=='P') {
children[i].classList.add('selected');
}
}
.selected {
color: aqua;
}
<div id="content">
<h2>Heading 1</h2>
<p>p 1</p>
<p>p 2</p>
<h3>Heading 2</h3>
<p>p 3</p>
<p>p 4</p>
<h4>Heading 3</h4>
<p>p 5</p>
<p>p 6</p>
</div>

jquery get All from element to element

Is there any way to select all elements from given elements to another given elements?
example:
<div>
<div>
<a name="break1"></a>
<p> belongs to break 1</p>
<div>
<p> belongs to break 1</p>
</div>
</div>
<p>belongs to break 1</p>
<div>
<a name="break2"></a>
<p> belongs to break 2</p>
<div>
<p> belongs to break 2</p>
<div>
<p> belongs to break 2</p>
</div>
</div>
</div>
</div>
we need something like the following:
$('[name*="break1"]').selectAllUntil('[name*="break2"]');
and result should be:
<p> belongs to break 1</p>
<div>
<p> belongs to break 1</p>
</div>
<p>belongs to break 1</p>
Most important thing: need to look down the siblings of each element up to the element matched by the selector, if we didnt find it we need to keep searching at the parent node
this can be misleading because we need to select all until the next element, even though the next element exist in the parent elements
Assuming the html is always the same you could try using prevAll().
Example:
$('.break2').parent().prevAll().css('color', 'blue');
https://jsfiddle.net/kt10r6n5/1/
Note:
If you are using a class like you have in your demo then this might be problematic if you have multiple instances of that same class. You would then have to find the last instance of that class. This will work better with an id of course.
Docs:
http://api.jquery.com/prevall/
I created method in plunker:
https://plnkr.co/edit/XUNCkYMJz4TEekLdBrJM?p=preview
This script returns jquery object with collection of elements between selector1 and selector2.
EDIT. Ok i got in now! It should work as expected.
IMPORTANT - script is working on level of selector parent, so is dependend to html structure.
jQuery.fn.extend({
selectAllUntil: function(selector) {
var currentDiv=this.parent("div");
var elements = $();//empty jquery collection
while(currentDiv.length){
if (currentDiv.find(selector).length){
//if our element have inside selector then this is end
return elements;
}else{
//no selector so add
elements= elements.add(currentDiv);
}
//next element
currentDiv=currentDiv.next();
}
}});
//usage:
$(function(){
var elements=$('[name*="break2"]').selectAllUntil('[name*="break4"]');
elements.css("color","red");
});

Is there a way to check an element's attribute when using Event Delegation?

Using Event Delegation, is there a way to check the element that was fired if it has a particular attribute or specifically a class or an ID?
<ul>
<li><button>Make the first paragraph appear</button></li>
<li><button>Make the second paragraph appear</button></li>
<li><button>Make the third paragraph appear</button></li>
</ul>
<div>
<p class="first">First paragraph</p>
<p class="second">Second paragraph</p>
<p class="third">Third paragraph</p>
</div>
Let's say all the paragraphs are hidden initially and clicking on the first button, the first paragraph appears and clicking on the second button, the first paragraph is hidden and the second paragraph is displayed and when the third button is clicked, the second paragraph is hidden while keeping the first paragraph hidden as well.
My solution so far was to make a event handler for each specific button and hide the other two paragraphs while only showing one. It works but if the number of elements increased, the event handlers needed for each one would increase too. Is there a better way to do this?
If the index of buttons and paragraphs are the same then you can make use of .index():
$('button').click(function() {
var idx = $(this).index('ul li button');
$('div p').eq(idx).show().siblings('p').hide();
});
Fiddle Demo
or you can use data-* attribute if the index are different:
<ul>
<li><button data-parapgraph="first">Make the first paragraph appear</button></li>
<li><button data-parapgraph="second">Make the second paragraph appear</button></li>
<li><button data-parapgraph="third">Make the third paragraph appear</button></li>
</ul>
<div>
<p class="first">First paragraph</p>
<p class="second">Second paragraph</p>
<p class="third">Third paragraph</p>
</div>
then apply .data() to retrieve the data-* attribute:
$('button').click(function() {
var parapgraph = $(this).data('parapgraph');
$('p.' + parapgraph).show().siblings('p').hide();
});
Fiddle Demo
I think if you can make sure the position of the button and the p to be displayed are the same then you can use an index based solution like
jQuery(function ($) {
var $ts = $('div > p');
$('ul button').click(function (e) {
$ts.hide().eq($(this).parent().index()).show()
})
})
Demo: Fiddle
I would rather use <a> instead of buttons and then use the href attribute for the identifying, and use id's for paragraphs
<ul class="link-list">
<li>Make the first paragraph appear</li>
<li>Make the second paragraph appear</li>
<li>Make the third paragraph appear</li>
</ul>
<div>
<p id="first">First paragraph</p>
<p id="second">Second paragraph</p>
<p id="third">Third paragraph</p>
</div>
$('.link-list').on('click','li > a',function(){
//id would be something like #first
var id = $(this).attr('href');
//we use it as a selector (you can also use $('div.classname').find(id);
var $paragraph = $(id);
// we show our desired paragraph and hide its siblings
$paragraph.show().siblings().hide();
// make sure the browser does not follow the link/anchor
return false;
});
Fiddler

How to wrap an element with a div in jQuery?

What I want is to wrap all my p tags in a special div having a class p_wrapper. I want something like this:(For all elements not only for p and div)
$('p').wrappAll('.p_wrapper')
I should get this HTML:
Before
<p>This is a paragraph!</p>
<p>This is another paragraph!</p>
After
<div class="p_wrapper"> <p>This is a paragraph!</p> </div>
<div class="p_wrapper"> <p>This is another paragraph!</p> </div>
$("p").wrap($("<div/>").addClass("p_wrapper"));
http://jsfiddle.net/aXwDs/1/
$('p').wrap('<div class="p_wrapper"></div>');
This should work :
$('p').wrap('<div class="p_wrapper">');

Categories