I need to find out if there are other <div> or <iframe> elements in my current <div>, and separate them from other data. For example, I need to transfer HTML from this:
<div class="main">
<p>Some text<p>
<iframe src="test.com"/>
<p>Other text<p>
</div>
to this
<div class="main">
<p>Some text<p>
</div>
<iframe src="test.com"/>
<div class="main">
<p>Other text<p>
</div>
Also, the element that needs to be removed from the <div> may be at the end, so it will be enough to move the closing </div> tag in front of it instead of after it.
UPD:
I almost solved the problem, but one problem remained - how to embed ONLY the opening or closing tag in front or end of the element?
const splitter = function(element){
let newObj = document.createElement("div");
newObj.innerHTML = element.innerHTML;
[...newObj.querySelectorAll("div.typo")].forEach(ele => {
if(ele.querySelectorAll("div.incut").length != 0){
ele.querySelectorAll("div.incut").forEach(eles => {
eles.before('</div>') //here
eles.after('<div class="typo">') //and here
})
}
})
console.log(newObj)
};
console:
<div class="typo">
<p>Some text</p>
"</div>"<div class="incut"> Incut text</div>"<div class="typo">"
<p>Other text</p>
</div>
If I understand you correctly, here are the steps:
Declare a temp variable lastMain which will be the last .main div so the elements in the "queue" will append to it. Also isLastNodeIframe to identify if need to create a new .main.
Iterate through all the elements.
If it's an iframe, you put it out with parentNode.appendChild into its parent.
If it's an empty string which caused by code indentation, ignore.
If it's not an iframe:
If the previous tag was iframe, re-create .main.
Append the child to lastMain.
const app = document.querySelector('.app');
const main = document.querySelector('.main');
let lastMain;
let isLastNodeIframe = false;
Array.from(main.childNodes).forEach(child => {
if (isIframe(child)) {
isLastNodeIframe = true;
main.parentNode.appendChild(child);
} else if (isEmptyText(child)) {
return;
} else {
if (isLastNodeIframe) {
isLastNodeIframe = false;
lastMain = main.cloneNode(false);
main.parentNode.appendChild(lastMain);
}
if (lastMain) {
lastMain.appendChild(child);
}
}
});
function isIframe(element) {
return element.tagName === 'IFRAME';
}
function isEmptyText(element) {
return !element.tagName && !element.textContent.trim();
}
console.log(app.innerHTML)
<div class="app">
<div class="main">
<p>Some text</p>
<iframe></iframe>
<p>Other text1</p>
<p>Other text2</p>
text without parent
<iframe></iframe>
<p>Other text3</p>
<iframe></iframe>
</div>
</div>
If something is not clear, let me know.
Related
I'm trying to change the background of the body on my HTML doc using some basic JS functions. I have set the function to target a specific ID, and then the style.background tag, if it is the current background image, then set it to another one I specified, else keep the same image. I have tried changing the code countless times now, but still can't seem to get it to change the background. Any help with this would be appreciated.
HTML:
<div class="bg-image"
style="background-image: url('./img/frankie.jpg');
height: 100vh" id="bacgr"> <!--SETS BACKGROUND using id tag to change w/ JS-->
<main role="main" class="container d-flex justify-content-center">
<div class="starter-template">
<h1>Bootstrap starter template</h1>
<p class="lead">Use this document as a way to quickly start any new project.<br> All you get is this text and a mostly barebones HTML document.</p>
</div>
</main>
</div>
JS:
let myImage = document.getElementById('bacgr').style.backgroundImage; //have to target specific image(like an array ([0])), put inside div w/ id.
myImage.onclick = function() {
if(myImage === "url('./img/frankie.jpg')") {
myImage == "url('./img/jesus_mobile.jpg')";
} else {
myImage == "url('./img/frankie.jpg')";
}
}
Try this:
const el = document.getElementById('bacgr');
el.onclick = function() {
if(this.style.backgroundImage === "url('./img/frankie.jpg')") {
this.style.backgroundImage = "url('./img/jesus_mobile.jpg')";
} else {
this.style.backgroundImage = "url('./img/frankie.jpg')";
}
}
Here is the example
You are changing only the URL, but that will not be assigned back to the dom element.
const el = document.getElementById('bacgr');
let prev = 'url("https://images.unsplash.com/photo-1570215171323-4ec328f3f5fa")';
el.onclick = function() {
el.style.backgroundImage = prev === el.style.backgroundImage ? 'url("https://images.unsplash.com/photo-1583508915901-b5f84c1dcde1")' : prev;
}
<div class="bg-image" style="background-image: url('https://images.unsplash.com/photo-1570215171323-4ec328f3f5fa');
height: 100vh" id="bacgr">
<main role="main" class="container d-flex justify-content-center">
<div class="starter-template">
<h1>Bootstrap starter template</h1>
<p class="lead">Use this document as a way to quickly start any new project.<br> All you get is this text and a mostly barebones HTML document.</p>
</div>
</main>
</div>
What is the javascript in order to only display posts 3 & 4 in order???
Also I need it be dynamic so if I put a 5th post it will only display 4th and 5th posts... I was thinking about something like a date function or a simple incrementor but can't seem to figure it out. I'm new to javascript and have been trying different things but no avail... Thanks in advance...
<!DOCTYPE html>
<html>
<body>
<div id="posts-div">
<h1 class="post-title">post4</h1>
<p class="post">post4</p>
</div>
<div id="posts-div">
<h1 class="post-title">post3</h1>
<p class="post">post3</p>
</div>
<div id="posts-div">
<h1 class="post-title">post2</h1>
<p class="post">post2</p>
</div>
<div id="posts-div">
<h1 class="post-title">post1</h1>
<p class="post">post1</p>
</div>
<script>
// ???
</script>
</body>
</html>
You dont need script for that. You can do it with CSS.. I have changed your html little bit (made posts-div class in html).
.posts-div{
display:none;
}
.posts-div:nth-child(-n+2) {
display:block;
}
<!DOCTYPE html>
<html>
<body>
<div class="posts-div">
<h1 class="post-title">post5</h1>
<p class="post">post5</p>
</div>
<div class="posts-div">
<h1 class="post-title">post4</h1>
<p class="post">post4</p>
</div>
<div class="posts-div">
<h1 class="post-title">post3</h1>
<p class="post">post3</p>
</div>
<div class="posts-div">
<h1 class="post-title">post2</h1>
<p class="post">post2</p>
</div>
<div class="posts-div">
<h1 class="post-title">post1</h1>
<p class="post">post1</p>
</div>
<script>
// ???
</script>
</body>
</html>
You can test it on JSfiddle as well.. https://jsfiddle.net/nimittshah/b5eL3ykx/6/
$('.posts-div:gt(1)').hide()
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!DOCTYPE html>
<html>
<body>
<div class="posts-div">
<h1 class="post-title">post4</h1>
<p class="post">post4</p>
</div>
<div class="posts-div">
<h1 class="post-title">post3</h1>
<p class="post">post3</p>
</div>
<div class="posts-div">
<h1 class="post-title">post2</h1>
<p class="post">post2</p>
</div>
<div class="posts-div">
<h1 class="post-title">post1</h1>
<p class="post">post1</p>
</div>
</body>
Try this:
<script>
document.addEventListener("DOMContentLoaded", function () {
var allPosts = document.querySelectorAll(".posts-div");
// This is the number of posts you want displayed
var numberOfPostsToShow = 2;
for (var i = 0; i < allPosts.length; i++) {
if(i > numberOfPostsToShow - 1) {
allPosts[i].style.display = "none";
}
}
});
</script>
This way you will choose how many posts you want to be shown with the numberOfPostsToShow variable.
Let me know if this worked. Regards.
The way I interpreted your question, you need a way to:
show only the first n elements;
add new elements to the top of the list of posts, dynamically;
when you add them, update the visible elements.
Assuming a slightly modified version of your code, which corrects the id/class issue and adds a container for all the posts (this time with a proper id):
<!DOCTYPE html>
<html>
<body>
<div id="posts-container">
<div class="posts-div">
<h1 class="post-title">post4</h1>
<p class="post">post4</p>
</div>
<div class="posts-div">
<h1 class="post-title">post3</h1>
<p class="post">post3</p>
</div>
<div class="posts-div">
<h1 class="post-title">post2</h1>
<p class="post">post2</p>
</div>
<div class="posts-div">
<h1 class="post-title">post1</h1>
<p class="post">post1</p>
</div>
</div>
<script>
// ???
</script>
</body>
</html>
this code will do the trick and manage both the addition and the updates to the visibility of the posts:
function showOnly(visible, query){
var elements = document.querySelectorAll(query);
for (var i = 0; i < elements.length; i++){
if (i < visible - 1){
elements[i].style.display = 'block';
} else {
elements[i].style.display = 'none';
}
}
}
function publishPost(element, visible){
showOnly(visible, '#posts-container .posts-div')
var elements = document.querySelectorAll('#posts-container .posts-div');
element.style.display = 'block';
if (elements.length > 0) {
document.querySelector('#posts-container').insertBefore(element, elements[0]);
} else {
document.querySelector('#posts-container').appendChild(element);
}
}
The showOnly function (to be called with the number of elements to be shown and the string that identifies the elements with querySelectorAll) will only make visible the first n elements identified by the string. You can use it independently of the rest of the code if needed.
The publishPost function, on the other hand, is strictly dependent on the modified html above (to use it elsewhere you will need to adjust the strings fed to querySelector and querySelectorAll). It takes the element to be published as the first argument, the number of elements that need to be visible as the second. Then it updates the list of posts prepending the new one to it, and it also updates which posts are visible.
This is a code sample that uses it:
var elDiv = document.createElement('div');
var elH1 = document.createElement('h1');
var elP = document.createElement('p');
elDiv.classList = 'posts-div';
elH1.classList = 'post-title';
elP.classList = 'post';
elH1.innerText = 'some title';
elP.innerText = 'some text for the post';
elDiv.appendChild(elH1).appendChild(elP);
publishPost(elDiv, 2);
showOnly
This function starts by getting a list of the elements whose visibility must be managed:
var elements = document.querySelectorAll(query);
then it loops through the list and examines each element:
for (var i = 0; i < elements.length; i++){
if it has to be visible, it sets the style.display property to 'block':
if (i < visible){
elements[i].style.display = 'block';
otherwise it sets it to 'hidden':
else {
elements[i].style.display = 'none';
publishPost
This function starts by showing only n-1 elements (because it will need to add a new, visible element to the top of the list):
showOnly(visible - 1, '#posts-container .posts-div')
then it retrieve the current posts:
var elements = document.querySelector('#posts-container .posts-div');
it makes the new element visible:
element.style.display = 'block';
finally, it adds the element to the top of the list (the different syntax depends on wether the list is empty):
if (elements.length > 0) {
document.querySelector('#posts-container').insertBefore(element, elements[0]);
} else {
document.querySelector('#posts-container').appendChild(element);
}
I have this code that is generated by php:
<div class="fusion-post-content post-content">
<h2 class="blog-shortcode-post-title"></h2>
<p class="fusion-single-line-meta"></p>
<div class="fusion-post-content-container"></div>
</div>
I need to wrap two elements by using javascript so the code would look like this:
<div class="fusion-post-content post-content">
<div class="class">
<h2 class="blog-shortcode-post-title"></h2>
<p class="fusion-single-line-meta"></p>
</div>
<div class="fusion-post-content-container"></div>
</div>
This will do what you want. Isn't it better to change the code on server-side??
// Select the first element found
var parent = document.querySelector('.fusion-post-content');
console.log('Old child-length', parent.children.length);
console.log('Old:', parent.innerHTML);
// *You don't need the timeout
setTimeout(function () {
var h2 = parent.firstElementChild;
var p = parent.firstElementChild.nextElementSibling;
// Remove cildren
parent.removeChild(h2);
parent.removeChild(p);
// Insert the new child
parent.insertAdjacentHTML('afterbegin', '<div class="class"></div>');
// Insert the other children (old) in the new child
parent.firstElementChild.insertAdjacentElement('beforeend', h2);
parent.firstElementChild.insertAdjacentElement('beforeend', p);
// Gets one less, since we put to children in one (3 - 1 = 2)
console.log('New child-length', parent.children.length);
console.log('New:', parent.innerHTML);
}, 500);
<div class="fusion-post-content post-content">
<h2 class="blog-shortcode-post-title"></h2>
<p class="fusion-single-line-meta"></p>
<div class="fusion-post-content-container"></div>
</div>
My first goal is to split a string by spaces, but not the ones within html-tags.
I've tried to rewrite the following, unsuccessfully: Javascript split by spaces but not those in quotes
What would the regex look like in:
arr = fullHtmlString.split(?);
?
My main goal is to shift an IMG-tag by one space at a time.
After that I'll iterate over the array, search for the img-tag, remove it, and add it the next item, and finally join the array.
The code I use at the moment is quite comprehensive and use jQuery extensively to achive the goal.
Input:
<div>
<p><img class=something>Some text.</p>
<p>Some more text.</p>
</div>
Deisred output first time:
<div>
<p>Some<img class=something> text.</p>
<p>Some more text.</p>
</div>
...second time:
<div>
<p>Some text.<img class=something></p>
<p>Some more text.</p>
</div>
...third time:
<div>
<p>Some text.</p>
<p><img class=something>Some more text.</p>
</div>
You should not try to do this with a regular expression, why explained here.
You can use DOM properties and methods though
function run(){
var img = document.querySelector(".something"),
sibling = img,
parent = img.parentNode,
next = parent.nextElementSibling;
//Search for the next textNode
while((sibling = sibling.nextSibling) && sibling.nodeType !=3);
if(sibling) {
//split the text only once,
//so "some more text" becomes ["some","more text"]
var textParts = sibling.textContent.split(/ (.*)?/,2);
//put the first split item before the sibling
parent.insertBefore(document.createTextNode(textParts[0]+" "),sibling);
//replace the sibling with the img element
parent.replaceChild(img,sibling);
//finally if there was more text insert it after the img
textParts[1] && parent.insertBefore(document.createTextNode(textParts[1]),img.nextSibling);
} else if(!sibling && next) {
//no sibling in the current parent,
//so prepend it to the next available element in parent
next.insertBefore(img,next.firstChild);
} else {
clearInterval(timer);
}
}
var timer = setInterval(run,2000);
<div>
<p><img class="something" src="http://placehold.it/10x10">Some text.</p>
<p>Some <span>skip me</span> more text.</p>
</div>
I've run into a problem that I don't know how to solve.
I need to wrap each grouping of <h2> and <p> tags with a containing <article> tag.
My current HTML looks something like this:
<h2 category="someCategory">
<p>text text text<p>
<h2 category="anotherCategory">
<p>text text text<p>
<p>text text text<p>
I need to use javascript to make it look like this:
<article>
<h2 category="someCategory">
<p>text text text<p>
</article>
<article>
<h2 category="anotherCategory">
<p>text text text<p>
<p>text text text<p>
</article>
Somehow the javascript needs to figure out that each new <h2> tag is the start of a new article element. And then that and the last <p> tag before the next <h2> tag will be end of the article.
(The bigger picture is that I'm parsing a markdown document and need the <article> tags as css hooks for layout.)
I have no idea how to begin solving this problem, so I would be grateful of any help!!
d13
UPDATE: Thank you!! I've tried both answers and they both work perfectly!
This will also remove the old HTML tags.
var articles = [], article, sibling, toDelete = [];
var h2s = document.getElementsByTagName("H2");
for(var i = 0, h2; h2 = h2s[i++];){
article = document.createElement("article");
sibling = h2.nextElementSibling;
article.appendChild(h2.cloneNode(true));
while(sibling && sibling.tagName !== "H2"){
if(sibling.tagName === "P"){
article.appendChild(sibling.cloneNode(true));
toDelete.push(sibling);
}
sibling = sibling.nextElementSibling;
}
articles.push(article);
}
while(toDelete.length > 0){
toDelete[toDelete.length-1].parentNode.removeChild(toDelete[toDelete.length-1]);
toDelete.pop();
}
while(h2s.length > 0){
h2s[0].parentNode.removeChild(h2s[0]);
}
for(i = 0, article; article = articles[i++];){
document.body.appendChild(article);
}