I have a div element that I want to be printed on the page when I click a Create Button.
Thus, when I click create I call a function that has: document.getElementById("createdDiv").textContent = document.querySelector("[data-feed]");
This finds my div element and prints to the page [object HTMLDivElement]
However, when I print the element to the console, I get my div element:
<div data-feed class="feed-element" ... ></div>
I know the console has a toString function that converts the div element into a string but I am not sure how to do this in javascript so I can print the same string to the page. Any suggestions?
You could use outerHTML:
document.getElementById("createdDiv").textContent = document.querySelector("[data-feed]").outerHTML;
document.getElementById("createdDiv").textContent = document.querySelector("[data-feed]").outerHTML;
[data-feed]::before {
content: 'The source element: ';
color: #f00;
}
#createdDiv {
white-space: pre-wrap;
border: 1px solid #000;
padding: 0.5em;
border-radius: 1em;
}
<div data-feed="something"><span>Text in here</span> with <em>various</em> <strong>elements</strong></div>
<div id="createdDiv"></div>
In order to remove HTML from any childNodes, then you could use a function to clone the node, remove the children, and then return only the outerHTML of that specific node:
function tagHTMLOnly(elem) {
var temp = elem.cloneNode();
while (temp.firstChild) {
temp.removeChild(temp.firstChild);
}
return temp.outerHTML;
}
document.getElementById("createdDiv").textContent = tagHTMLOnly(document.querySelector("[data-feed]"));
function tagHTMLOnly(elem) {
var temp = elem.cloneNode();
while (temp.firstChild) {
temp.removeChild(temp.firstChild);
}
return temp.outerHTML;
}
document.getElementById("createdDiv").textContent = tagHTMLOnly(document.querySelector("[data-feed]"));
[data-feed]::before {
content: 'The source element: ';
color: #f00;
}
#createdDiv {
white-space: pre-wrap;
border: 1px solid #000;
padding: 0.5em;
border-radius: 1em;
}
<div data-feed="something"><span>Text in here</span> with <em>various</em> <strong>elements</strong>
</div>
<div id="createdDiv"></div>
References:
Element.outerHTML.
Related
I have a <div> wrapped around a set of nested <div> elements; I'd like to present this as a drop-down menu. The element is part of a plugin I'm using on a WordPress website but this should still be adjustable using custom script.
I would like to replace the encasing div with a <ul>, and turn all of the inner <div>s into <li> elements, so I can show this as a drop-down. Either that or a <select>, with nested <option> tags.
Is there a way I can change the HTML tag type , or replace the div, using pure JavaScript, or jQuery?
From:
<div class="slots">
<div class="availableslot"></div>
<div class="availableslot"></div>
</div>
to:
<ul class="slots">
<li class="availableslot"></li>
<li class="availableslot"></li>
</ul>
or:
<select class="slots">
<option class="availableslot"></option>
<option class="availableslot"></option>
</select>
The isolated code: https://codepen.io/bolti95/pen/rNpMdJx
//div into list
const slots_list = document.createElement("ul");
var slots = document.getElementsByClassName("slots")
console.log(slots)
slots.insertBefore(slots_list, slots.children[0])
.slots {
width: 100% !important;
height: max-content;
border: none !important;
box-shadow: none !important;
margin: auto;
}
.slots div a {
color: #77635A;
background: none !important;
}
.slots div a:hover {
background-color: #F7F7F7 !important;
transition: 0.6s;
}
.availableslot {
width: 120px;
height: 55px;
border-radius: 30px;
margin-bottom: 25px;
margin: 10px;
border: none !important;
border-width: 0px;
text-align: center;
font-size: 20px !important;
background-color: #EFE8DA;
color: #77635A;
}
.availableslot a {
margin: 10px;
}
.availableslot:hover {
outline: 1px solid #5FDEAB;
background-color: #F7F7F7;
transition: 0.6;
}
.availableslot a:hover {
background-color: #F7F7F7;
color: #5FDEAB;
transition: 0.6s;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="<?php bloginfo('charset'); ?>">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<div class="slots">
<span>03/22/2022</span>
<br>
<div class="availableslot"><a>11:00</a></div>
<div class="availableslot"><a>12:00</a></div>
<div class="availableslot"><a>13:00</a></div>
<div class="availableslot"><a>14:00</a></div>
<div class="availableslot"><a>15:00</a></div>
</div>
</body>
</html>
To convert the .availableslot div elements to a ul/li list you can use a combination of wrapAll() and replaceWith(), like this:
$('.availableslot').wrapAll('<ul class="slots" />').replaceWith(function() {
return `<li class="availableslot">${this.innerHTML}</li>`
});
$('.availableslot').wrapAll('<ul class="slots" />').replaceWith(function() {
return `<li class="availableslot">${this.innerHTML}</li>`
});
.slots {
width: 100% !important;
height: max-content;
border: none !important;
box-shadow: none !important;
margin: auto;
}
.slots div a {
color: #77635A;
background: none !important;
}
.slots div a:hover {
background-color: #F7F7F7 !important;
transition: 0.6s;
}
.availableslot {
width: 120px;
height: 55px;
border-radius: 30px;
margin-bottom: 25px;
margin: 10px;
border: none !important;
border-width: 0px;
text-align: center;
font-size: 20px !important;
background-color: #EFE8DA;
color: #77635A;
}
.availableslot a {
margin: 10px;
}
.availableslot:hover {
outline: 1px solid #5FDEAB;
background-color: #F7F7F7;
transition: 0.6;
}
.availableslot a:hover {
background-color: #F7F7F7;
color: #5FDEAB;
transition: 0.6s;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="slots">
<span>03/22/2022</span>
<br>
<div class="availableslot"><a>11:00</a></div>
<div class="availableslot"><a>12:00</a></div>
<div class="availableslot"><a>13:00</a></div>
<div class="availableslot"><a>14:00</a></div>
<div class="availableslot"><a>15:00</a></div>
</div>
The same technique can be used to convert them to a select/option form control:
$('.availableslot').wrapAll('<select />').replaceWith(function() {
let time = this.querySelector('a').innerText;
return `<option value="${time}">${time}</option>`;
});
$('.availableslot').wrapAll('<select />').replaceWith(function() {
let time = this.querySelector('a').innerText;
return `<option value="${time}">${time}</option>`;
});
.slots {
width: 100% !important;
height: max-content;
border: none !important;
box-shadow: none !important;
margin: auto;
}
.slots div a {
color: #77635A;
background: none !important;
}
.slots div a:hover {
background-color: #F7F7F7 !important;
transition: 0.6s;
}
.availableslot {
width: 120px;
height: 55px;
border-radius: 30px;
margin-bottom: 25px;
margin: 10px;
border: none !important;
border-width: 0px;
text-align: center;
font-size: 20px !important;
background-color: #EFE8DA;
color: #77635A;
}
.availableslot a {
margin: 10px;
}
.availableslot:hover {
outline: 1px solid #5FDEAB;
background-color: #F7F7F7;
transition: 0.6;
}
.availableslot a:hover {
background-color: #F7F7F7;
color: #5FDEAB;
transition: 0.6s;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="slots">
<span>03/22/2022</span>
<br>
<div class="availableslot"><a>11:00</a></div>
<div class="availableslot"><a>12:00</a></div>
<div class="availableslot"><a>13:00</a></div>
<div class="availableslot"><a>14:00</a></div>
<div class="availableslot"><a>15:00</a></div>
</div>
One approach, using plain JavaScript, is below; explanatory comments are in the code:
// creating a named function, using Arrow syntax, that allows the user to pass in an
// Object of updated preferences:
const replaceWith = (opts) => {
let settings = {
// String, sets the element-type to replace the outer wrapper:
outerTo: 'select',
// String, sets the element-type to replace the inner wrapper:
innerTo: 'option',
// String, CSS selector to select the relevant elements:
source: '.slots',
// Boolean, true: replaces the element when its replacement is inserted into the document,
// false: hides the element when its replacement is inserted into the document:
replace: true,
// Boolean, true: preserves the child-nodes of the inner-elements being replaced,
// false: preserves the text-content of the inner-element, but does not
// keep the child-nodes that may have contributed text:
preserveHTMLChildren: false,
};
// while I'm sure there's an easy way to use destructruring to avoid this step, I chose to
// use this approach, wherein we take the keys of the opts Object passed to the function
// or - if no Object is passed - we look at the empty Object-literal (to avoid errors),
// we then iterate over the Array of Object-keys using Array.prototype.forEach(), again
// using Arrow syntax:
Object.keys(opts || {}).forEach(
// the 'key' passes in a reference to the current key of the Array of Object-keys
// over which we're iterating, and in the function we set the Object-key of the
// settings Object to the value of the opts Object key-value:
(key) => settings[key] = opts[key]
);
// to reduce unnecessary typing, we alias the document to the D variable:
let D = document,
// we create an element according to the desired elements:
outer = D.createElement(settings.outerTo),
inner = D.createElement(settings.innerTo),
// we use document.querySelectorAll(), using the supplied, or default,
// CSS selector, to retrieve all matching elements:
sources = [...D.querySelectorAll(settings.source)];
// we use NodeList.prototype.forEach() to iterate over the NodeList of matching
// elements returned to the sources variable:
sources.forEach(
// using Arrow syntax we pass in a reference to the current Node ('source') of
// the NodeList:
(source) => {
// we clone the created-element for the outer-element's replacement:
let outerClone = outer.cloneNode();
// we copy the classList:
outerClone.classList = source.classList;
// we use an Array-literal with the spread syntax to create an Array of
// the child-nodes of the current 'source', and then iterate over that
// Array of Nodes using Array.prototype.forEach():
[...source.children].forEach(
// we pass in a reference to the current child-element of the parent
// 'source', in the variable named 'child':
(child) => {
// we clone the created-element for the inner element:
let innerClone = inner.cloneNode();
// if settings.preserveHTMLChildren is exactly equal to Boolean true:
if (settings.preserveHTMLChildren === true) {
// while there is a firstChild node (whether comment, text, HTMLElement...):
while (child.firstChild) {
// we copy that firstChild Node accross to the innerClone element:
innerClone.append(child.firstChild);
}
} else {
// we update the text-content of the innerClone to be equal to the
// original text-content:
innerClone.textContent = child.textContent;
}
// copying the classList of the existing child to the innerClone:
innerClone.classList = child.classList;
// we append the current innerClone to the outerClone:
outerClone.append(innerClone);
});
// we then access the current 'source' element's parentNode, and
// use ParentNode.insertBefore() to insert the newly-created
// outerClone before the current 'source' element's next-sibling
// (effectively inserting it after the current 'source' element):
source.parentNode.insertBefore(outerClone, source.nextSibling);
// if the user wants to replace the element, so settings.replace is
// exactly-equal to (Boolean) true:
if (settings.replace === true) {
// we remove the current 'source' element, using Node.remove():
source.remove();
} else {
// otherwise we set source.hidden to 'true' to hide the element
// using the 'hidden' HTML attribute (though in the demo I chose
// to modify the opacity to show that the element does, in fact,
// remain in the document); this should probably be modified when
// in use:
source.style.opacity = 0.4;
// source.hidden = true;
}
})
};
// calling the function:
replaceWith({
// using a non-default CSS Selector:
source: '#demo1'
});
// calling the functiona again:
replaceWith({
// modifying the CSS selector again:
source: '#demo2',
// using a <ul> to 'replace' the outer element:
outerTo: 'ul',
// using an <li> to wrap the inner contents:
innerTo: 'li',
// setting Boolean false, to not replace the original
// element(s), so inserting the new element(s) and hiding
// the original(s):
replace: false,
});
replaceWith({
source: '#demo3',
outerTo: 'ul',
innerTo: 'li',
// setting preserveHTMLChildren to true means the <span> elements
// contained within the inner-<div> elements will be preserved when
// the HTML is modified:
preserveHTMLChildren: true,
});
replaceWith({
source: '#demo4',
// again, setting preserveHTMLChildren to true means the <span> elements
// contained within the inner-<div> elements will be preserved when
// the HTML is modified; but while those <span> elements may be present
// in the DOM (in FF 98/Ubuntu 21.10), they are not styled by the CSS
// (and an <option> element has no valid child-elements), so this isn't
// necessarily going to be respected by the browser:
preserveHTMLChildren: true,
});
*,
::before,
::after {
box-sizing: border-box;
font: normal 400 1rem / 1.5 sans-serif;
list-style-type: none;
margin: 0;
padding: 0;
}
.slots {
border: 1px solid palegreen;
margin: 1em auto;
padding: 0.5em;
width: 20rem;
}
.slots:not([id]) {
border: 1px solid rebeccapurple;
}
.slots span {
color: #f90;
}
<div id="demo1" class="slots">
<div class="availableslot">available 1</div>
<div class="availableslot">available 2</div>
</div>
<div id="demo2" class="slots">
<div class="availableslot">available 3</div>
<div class="availableslot">available 4</div>
</div>
<div id="demo3" class="slots">
<div class="availableslot">available <span>5</span></div>
<div class="availableslot">available <span>6</span></div>
</div>
<div id="demo4" class="slots">
<div class="availableslot">available <span>7</span></div>
<div class="availableslot">available <span>8</span></div>
</div>
JS Fiddle demo.
I have to admit that I'm curious as to why the <select> elements aren't being positioned correctly (in terms of margin-inline: auto), but that's a problem for another time.
If there are any questions then please don't hesitate to leave a comment below.
References:
Array.prototype.forEach().
Array literals [ /*...*/ ].
Arrow functions.
document.createDocumentFragment().
document.createElement().
document.querySelectorAll().
Element.children().
Element.classList.
Element.remove().
Node.cloneNode().
Node.firstChild.
Node.insertBefore().
Node.parentNode.
NodeList.prototype.forEach().
Object.keys().
Spread syntax ....
while (...) {...} statement.
I can't implement the check function if (element.classList.contains(__active)). That is, if the element already contains the class "active", then you need to remove this class from the element when you click on it.
Codepen
You can remove without checking. Try this:
const clickHandler = () => {
document.querySelector('.active')?.classList.remove('active');
};
div {
color: red;
background: black;
margin-bottom: 16px
}
div.active {
color: black;
background: red;
}
<div class="active">CHNAGE ME</div>
<button onClick="clickHandler()">CLICK TO CHANGE</button>
I'v a problem with my contenteditable div. I am currently trying to detect any change in my div element. This works quite well so far. But it fails when I change the content via jQuery:
jQuery(document).ready(function($) {
let input = $("#input");
input.on("input", function() {
console.log($(this).html().length);
});
$("button").click(function() {
input.html(input.html() + `<span class="emoji">😅</span>`);
});
});
div {
border: 1px solid #aaaaaa;
padding: 8px;
border-radius: 12px;
margin-bottom: 20px;
}
[contenteditable=true]:empty:before {
content: attr(placeholder);
display: block;
color: #aaaaaa;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="input" placeholder="Schreib eine Nachricht..." contenteditable="true" spellcheck="true"></div>
<button>Add element to contenteditable div</button>
How can I solve this problem? I could do this check inside my click event but I need to add a lot of them so I don't want to do this every time. In this case I think it's better to do it in one input check function.
In this case you will need to trigger the event you're listening to yourself:
jQuery(document).ready(function($) {
let input = $("#input");
input.on("input", function() {
console.log($(this).html().length);
// Contenteditable adds a <br> when empty.
// Solutions on SO appear not to work
if (!$(this).text()) {
console.log('cleared editable');
input.html('');
}
});
$("button").click(function() {
input.html(input.html() + `<span class="emoji">😅</span>`);
input.trigger('input');
});
});
[contenteditable=true] {
border: 1px solid #aaaaaa;
padding: 8px;
border-radius: 12px;
margin-bottom: 20px;
}
[contenteditable=true]:empty:before {
content: attr(placeholder);
display: block;
color: #aaaaaa;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="input" placeholder="Schreib eine Nachricht..." contenteditable="true" spellcheck="true"></div>
<button>Add element to contenteditable div</button>
If you don't want to add function inside your .click() listener you could achieve your effect by using MutationObserver API:
jQuery(document).ready(function($) {
let input = $("#input");
input.on("input", function() {
console.log($(this).html().length);
});
$("button").click(function() {
input.html(input.html() + `<span class="emoji">😅</span>`);
});
const targetNode = document.getElementById('input');
const config = { attributes: true, childList: true, subtree: true };
const callback = function(mutationsList, observer) {
// Use traditional 'for loops' for IE 11
for(let mutation of mutationsList) {
if (mutation.type === 'childList') {
console.log('A child node has been added or removed.');
}
else if (mutation.type === 'attributes') {
console.log('The ' + mutation.attributeName + ' attribute was modified.');
}
}
};
// Create an observer instance linked to the callback function
const observer = new MutationObserver(callback);
// Start observing the target node for configured mutations
observer.observe(targetNode, config);
});
div {
border: 1px solid #aaaaaa;
padding: 8px;
border-radius: 12px;
margin-bottom: 20px;
}
[contenteditable=true]:empty:before {
content: attr(placeholder);
display: block;
color: #aaaaaa;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="input" placeholder="Schreib eine Nachricht..." contenteditable="true" spellcheck="true"></div>
<button>Add element to contenteditable div</button>
I don't think this is most optimal way, maybe you should rethink your architecture. But I'm sure this one will fit your requirements.
I wrote the below code to get a shadow DOM with text and button, both text and button had been styled correctly, while the host background had been styled in a strange way,
the code is:
// Create a new object based of the HTMLElement prototype
var fonixDiv = Object.create(HTMLElement.prototype);
// Set up the element.
fonixDiv.createdCallback = function() {
// Create a Shadow Root
var shadow = this.createShadowRoot();
shadow.innerHTML = '\
<style>\
:host { \
border: 2px dashed red;\
text-align: left;\
font-size: 28px;\
background: blue;\
}\
h3 { color: red; }\
button { color: green; }\
</style>\
<h3>Shadow DOM</h3>\
<button id="d">click</button>\
';
shadow.children.d.addEventListener('click', function(e) {
this.textContent = "you clicked me :(";
shadow.children[1].textContent="Shadow DOM content changed";
host.style.background = "green";
alert("All: button, text and host should be change");
});
};
the output is in the attached.
any help pls.
I was able to solve the issue using css file:
the .html file is:
<fonix-div></fonix-div>
<div id="host1"></div>
the .js file is:
// Create a new object based of the HTMLElement prototype
var fonixDiv = Object.create(HTMLElement.prototype);
// Set up the element.
fonixDiv.createdCallback = function() {
// Create a Shadow Root
var shadow = this.createShadowRoot();
shadow.innerHTML = '<button id="d">click</button>';
shadow.addEventListener('click', function(e) {
console.log('1: '+this.host.dataset.disabled);
this.host.dataset.disabled='true'; // set Attribute to the custom element
});
shadow.children.d.addEventListener('click', function(e) {
this.textContent = "you clicked me :(";
shadow.children[1].textContent="Shadow DOM content changed";
this.disabled=true;
alert("All: button, text and host should be change");
});
};
// Register the new element.
var Xfonix =document.registerElement('fonix-div', {prototype: fonixDiv});
var thehost = document.querySelector('#host1');
thehost.appendChild(new Xfonix());
the .css file is:
body {background: #F7F7F7;}
fonix-div {
display: inline-block;
width: 200px;
height: 200px;
float: left;
margin: 0.5em;
border-radius: 3px;
background: #FFF;
box-shadow: 0 1px 3px rgba(0,0,0,0.25);
font-family: Helvetica, arial, sans-serif;
-webkit-font-smoothing: antialiased;
}
fonix-div:hover, fonix-div[data-disabled='true']:hover {background: red;}
fonix-div:Active {background: green;}
fonix-div[data-disabled='true'] {background: green;}
fonix-div::shadow p{color: blue;}
the output can be seen here:
I have different attributes which contains identical text strings. Like:
<div id="outer">
<div id="carousel-example-generic"></div>
<div data-target="#carousel-example-generic"></div>
<a href="#carousel-example-generic" ></a>
</div>
How do I find and rename "carousel-example-generic" on those elements using jquery?
You could do something like:
$(document.documentElement).html(function(i,val){
return val.replace(/carousel-example-generic/g,'my-new-string');
});
...but be careful, this will replace the string everywhere in the document: tags, text, values, everywhere. Also, as Salman says below, event handlers are also destroyed, including those of parent elements.
Given your example above, the code shown here yields the following html:
<div id="outer">
<div id="my-new-string">asdfasdf</div>
<div data-target="#my-new-string">asdfadsfad</div>
asdfasdfa
</div>
Here's a fiddle. Examine the elements to see the changes.
The following is one approach:
// iterates over each of the descendant elements of '#outer':
$('#outer *').each(function() {
// iterates over the array-like 'this.attributes' (list of the element's attributes)
// property, using Array.prototype.forEach, 'a' is the array-element itself:
Array.prototype.forEach.call(this.attributes, function(a) {
// setting the value of the attribute:
// if the string 'carousel-example-generic' is found within the attribute-value,
// we replace that string with 'new-string', otherwise
// return the current attribute-value:
a.value = a.value.indexOf('carousel-example-generic') > -1 ? a.value.replace('carousel-example-generic', 'new-string') : a.value;
});
});
$('#outer *').each(function() {
Array.prototype.forEach.call(this.attributes, function(a) {
a.value = a.value.indexOf('carousel-example-generic') > -1 ? a.value.replace('carousel-example-generic', 'new-string') : a.value;
});
});
$('#result').text($('#outer').html());
#result {
white-space: pre-wrap;
font-family: monospace;
border: 1px solid #f90;
margin-top: 1em;
position: relative;
padding: 0.5em;
}
#result::before {
content: 'HTML converted to:';
border: 1px solid #f90;
background-color: #fff;
position: absolute;
top: -1em;
left: 0.5em;
height: 2em;
line-height: 2em;
padding: 0 0.2em;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="outer">
<div id="carousel-example-generic"></div>
<div data-target="#carousel-example-generic"></div>
</div>
<div id="result"></div>
References:
JavaScript:
Array.prototype.forEach().
Element.attributes.
Function.prototype.call().
String.indexOf().
jQuery:
each().