Place a button and its div with one command - javascript

Currently, I have a button class which lets me place a clickable button inside a sentence, and a div class which lets me add content to the button which I placed at the end of the paragraph containing the sentence.
This is an example of how I use them
Try to click <button class="col">THIS</button> and see what happens.
<div class="con">nice!</div>
Did you try?
When this text is displayed on the page, the two sentences are placed inside two different paragraphs, so the div object is placed between them.
Here is a snippet with the css classes and the javascript.
( function() {
coll = document.getElementsByClassName("col");
conn = document.getElementsByClassName("con");
var i;
for (i = 0; i < coll.length; i++) {
coll[i].setAttribute('data-id', 'con' + i);
conn[i].setAttribute('id', 'con' + i);
coll[i].addEventListener("click", function() {
this.classList.toggle("active");
var content = document.getElementById(this.getAttribute('data-id'));
if (content.style.maxHeight) {
content.style.maxHeight = null;
} else {
content.style.maxHeight = content.scrollHeight + "px";
}
});
}
} )();
.col {
cursor: help;
border-radius: 0;
border: none;
outline: none;
background: none;
padding: 0;
font-size: 1em;
color: red;
}
.con {
padding: 0 1em;
max-height: 0;
overflow: hidden;
transition: .3s ease;
background-color: yellow;
}
Try to click <button class="col">THIS</button> and see what happens.
<div class="con">nice!</div>
Did you try?
I wonder if it is possible to implement a shortcut to place the two objects with one command, that is to obtain the previous example by using something like this
Try to click [[THIS|nice!]] and see what happens.
Did you try?
What I mean is that the command [[THIS|nice!]] should place the object <button class="col">THIS</button> in the same position and the object <div class="con">nice!</div> at the end of the paragraph containing the command.
Is it possible to implement such a command (or a similar one)?
EDIT
I forgot to say that the content of the button, ie what is written inside the div, should also be possible to be a wordpress shortcode, which is a shortcut/macro for a longer piece of code or text.

Using jQuery, closest() find the nearest <p> element and add <div class="con">nice!</div> after <p> element. To toggle you can use class active and add or remove .con element.
$('.col').click(function(){
let traget = $(this).closest('p');
if(traget.hasClass('active')) {
traget.removeClass('active');
traget.next('.con').remove();
} else {
traget.addClass('active');
traget.after(`<div class="con">${$(this).data('message')}</div>`);
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p>Try to click <button class="col" data-message="Hello">THIS</button> and see what happens.</p>
<p>Did you try?</p>

You usually dont use div to type text. you use it to define areas or group items. you could obtain what youre asking for in a 1 sentence like this:
html
<h1> some random text <a class="btnID">button</> some more text<h1>
css
.btnID {
color: red;
}

Related

why is only my first div changing colors?

i'm trying to change each of my div's background color to black when my mouse enters the div using an eventlistener. it currently is only switching the first div to black but not any other div. why is my eventlistener only applying to the first 'contentDivs' div?
example:
this is my html code:
<body>
<div id="mainContainer"></div>
<script src="index.js"></script>
</body>
this is my javascript code:
for(x=0; x<64; x++) {
const contentDivs = document.createElement('div');
contentDivs.id = 'contentDivs';
document.getElementById('mainContainer').appendChild(contentDivs);
}
document.getElementById('contentDivs').addEventListener('mouseenter', () => {
document.getElementById('contentDivs').style.background = 'black';
})
this is what shows up when inspecting the elements in google chrome:
If you want to select many elements you can use querySelectorAll. If you want to select one please use querySelector.
I've used minimal changes for your example. Ideally you should take advice of comments and change the identifying method to be class rather than id.
Lastly, I've added another effect using css alone, so you can compare how to make style changes using :hover class.
for (x = 0; x < 64; x++) {
const contentDivs = document.createElement('div');
contentDivs.id = 'contentDivs';
document.getElementById('mainContainer').appendChild(contentDivs);
}
document.querySelectorAll('#contentDivs').forEach(function(elem) {
elem.addEventListener('mouseenter', () => {
elem.style.background = 'black';
})
})
#contentDivs {
width: 20px;
height: 20px;
background: pink;
float: left;
margin: 10px;
transition: 500ms all;
}
#contentDivs:hover {
transform: scale(1.5);
}
<body>
<div id="mainContainer"></div>
</body>

Cannot get element with the "display: none" property applied to it using getElementById()

I'm running into an issue where the getElementById() function is unable to get a particular element on the page that has the display: none property applied to it, even though it's visible in the DOM (I can see that the div and its id exists on the final rendered page).
Is there a way around this?
Here's the code:
togglePanel() {
const panelId = this.accordionItem.querySelector("#collapsible-panel");
this.shouldShowAccordion = !this.shouldShowAccordion;
if (this.shouldShowAccordion) {
panelId.classList.remove("collapsed");
}
else {
panelId.classList.add("collapsed");
}
}
"collapsible-panel" is the ID of the div which has display: none applied to it.
setTimeout(() => {
document.querySelector("div[id='collapsiblepanel']").style.display = 'block';
}, 2000)
#collapsiblepanel {
width: 200px;
height: 100px;
background-color: red;
display: none;
}
Following is ana example where I am selecting a div which has the value of display as none. After 2 seconds I am setting up it's display to block.
<div id="collapsiblepanel">
</div>

Get content based on CSS pseudo class

I have a CSS declaration as follows:
span.boshbashbosh:nth-child(1):active:after {
content: 'FC';
}
I am trying to access the content (FC) it by using:
var content = window.getComputedStyle(document.getElementsByClassName("boshbashbosh:nth")[0], '::active').getPropertyValue('content');
alert(content);
However, all the alert does is show normal or none
Any advice on how to do this in plain JS? If I had 1000 of these, I wouldn't want to click/hover each one, is there a way I could dump some code into the developer console to do this?
There are a few issues here, the main one being that the CSS selector will only return an active element during a click interaction by the user, seeing that a click interaction causes the target element to become :active.
With that in mind, you could wrap your login in a mousedown element as shown below to extract the expected content value while the corresponding span element is :active as shown:
document.addEventListener("mousedown", () => {
/* When mouse down occours, look for the element that we want to read
pseudo content from */
var element = document.querySelector(".boshbashbosh:nth-child(1):active");
if (element) {
/* If the target element is active, read the content of the ::after
pseudo element */
var content = window.getComputedStyle(element, ":after")
.getPropertyValue("content");
alert(content);
}
})
span.boshbashbosh:nth-child(1):active:after {
content: 'FC';
}
/* Added for clarity/usability of snippet */
span {
background: pink;
margin: 1rem 0;
padding: 1rem;
display: block;
height: 1rem;
}
span.boshbashbosh:active {
background: yellow;
}
<p>Clicking first box alerts the ::after content</p>
<div>
<span class="boshbashbosh"></span>
<span class="boshbashbosh"></span>
<span class="boshbashbosh"></span>
</div>
I've also replaced the getElementsByClassName() call with querySelector() to simplify the code. Hope that helps!
Update
To access the content of multiple pseduo elements, you could adapt the snippet above as follows:
document.querySelectorAll(".boshbashbosh").forEach((element) => {
var content = window.getComputedStyle(element, ":after")
.getPropertyValue("content");
console.log(content);
});
span.boshbashbosh:nth-child(1):after {
content: 'FC';
}
span.boshbashbosh:nth-child(2):after {
content: 'EB';
}
span.boshbashbosh:nth-child(3):after {
content: 'DA';
}
<div>
<span class="boshbashbosh"></span>
<span class="boshbashbosh"></span>
<span class="boshbashbosh"></span>
</div>

Dynamic mouseenter

I appended a few divs with inside img tags. Every tag has own unique id = "theImg"+i where "i" is number. I want to mouseover on specific img and show the content of span (which also have specific id with number). Here is my code so far but not working.
var j;
document.onmouseover = function(r) {
console.log(r.target.id);
j = r.target.id;
}
$(document).on({
mouseover: function(e){
$("span").show();
},
mouseleave: function(e){
$("span").hide();
}
}, "img#"+j);
If you have a span after every img, maybe it's a good idea to not use JavaScript at all? ;-)
You could use :hover pseudoclass in CSS, making your thing always work reliably.
Consider the following example:
img + span {
display: none;
}
img:hover + span {
display: block;
}
/*/ Optional styles /*/
div {
position: relative;
float: left;
}
div img + span {
position: absolute;
color: #fff;
background: #27ae60;
border: solid 1px #2ecc71;
border-radius: 50px;
z-index: 1;
bottom: 1em;
width: 80%;
left: 50%;
margin-left: -43%;
padding: 2% 3%;
text-align: center;
}
<div>
<img src="https://placehold.it/400x200">
<span>This is an image of a gray rectangle!</span>
</div>
<div>
<img src="https://placehold.it/200x200">
<span>This is an image of a gray square!</span>
</div>
<div>
<img src="https://placekitten.com/g/400/200">
<span>This is an image of a cute kitten inside a rectangle!</span>
</div>
<div>
<img src="https://placekitten.com/g/200/200">
<span>This is an image of even cuter kitten inside a square!</span>
</div>
So the issue is that you are trying to set your handler on a dynamic selector ("img#"+j) but this will not work. For one thing, that equation will be evaluated only once, on page load, when j is undefined.
So you want to do this instead:
target only img tags for your mouse over... Better yet, give your special images all the same css class so you can attach the event handlers only to those. That will be more efficient.
When an image is moused over or out of, grab it's id attribute, extract the number from it, then use that to build a selector for the appropriate span to show.
var get_span_from_image = function(image) {
var image_id = image.attr("id");
var matches = image_id.match(/theImg(\d+)/);
if(matches) return $("theSpan" + matches[1]);
return $(); // nothing found, return an empty jQuery selection
};
$("img").hover(
function() { // mouse over
get_span_from_image($(this)).show();
},
function() { // mouse out
get_span_from_image($(this)).hide();
}
);
Note: There are better ways to "link" two nodes together, but this is just to answer your question with the current structure you have.
UPDATE: Some ideas to link two nodes together
So instead of trying to extract a number from an id attribute, a better way would be to tell either one of the image or span about it's sibling. You could output your html like this, for instance:
<img id="theImg1" data-target="theSpan1" class="hoverable" src="..."/>
....
<span id="theSpan1">...</span>
Of course now your ideas could be anything - you don't have to use numbered values or anything.
Then your hover code becomes quite simply:
var get_span_from_image = function(image) {
var span_id = image.data("target");
return $("#" + span_id);
};
$("img").hover(
function() { // mouse over
get_span_from_image($(this)).show();
},
function() { // mouse out
get_span_from_image($(this)).hide();
}
);
Hope this helps!

Javascript show/hide div requires two clicks

I've got code to show/hide two divs based upon the clicking of an arrow. However, the second div requires two clicks before it hides and the arrow doesn't change as expected. The top div works perfectly. Any suggestions about what I'm doing wrong, please?
EDIT - Thanks to the two posters who have pointed out my naming error in the arrows. However, after loading the page, the second div still requires two clicks before it toggles.
HTML
<div id="start_conditions_arrow" class="arrow_down" onclick="toggleDiv('start_conditions')"></div>
<h2>Starting Conditions</h2>
<div id="start_conditions">
<%= render :partial => 'start_conditions', :object => #page.start_conditions %>
</div>
<div id="probability_arrow" class="arrow_right" onclick="toggleDiv('probability_inputs')"></div>
<h2>Probability Inputs</h2>
<div id="probability_inputs">
<%= render :partial => 'probability_inputs', :object => #page.probability_inputs %>
</div>
Javascript
var toggleDiv = function(id){
var tag = document.getElementById(id).style;
if(tag.display == 'none'){
document.getElementById(id).style.display='block';
document.getElementById(id + '_arrow').className='arrow_down';
} else {
document.getElementById(id).style.display='none';
document.getElementById(id + '_arrow').className='arrow_right';
}
};
CSS
.arrow_down, .arrow_right {
width: 0;
height: 0;
margin: 12px 12px 0 0;
float: left;
cursor: pointer;
}
.arrow_down {
border-left: 10px solid transparent;
border-right: 10px solid transparent;
border-top: 18px solid #d5d5d5;
}
.arrow_right {
border-top: 10px solid transparent;
border-bottom: 10px solid transparent;
border-left: 18px solid #d5d5d5;
}
#probability_inputs {
display: none;
}
#start_conditions {
display: none;
}
Here's a Codepen including the above: https://codepen.io/anon/pen/yXozYJ
The second arrow element should have probability_inputs_arrow as id and not probability_arrow, as you're building its id in the function as id + '_arrow' and you pass 'probability_inputs'.
I know that I'm a little late answering this, but I have a solution. There are two issues that should be addressed and they're as follows:
The ID of the last div is incorrect as stated in PSR's answer.
The check against the style attribute of the element in the toggleDiv method doesn't account for the lacking of the style attribute upon page load.
Regarding the first issue, the ID should read probability_inputs_arrow, which fixes the issue with the arrow not being updated when it is toggled. However, the second issue needs a little more explaining...
Upon page load, the CSS provided is applied to the elements with the IDs probability_inputs and start_conditions, giving them both a display value of none. Upon clicking on either one of these arrows, you're retrieving the style attributes of the targeted element, and checking the display property which upon page load is empty. This is why it requires two clicks to achieve the desired behaviour, because the first click is effectively just setting that value in the else statement.
Here's a link to an updated Codepen that contains the fixes: https://codepen.io/anon/pen/oweELy
actually you are getting error here
The second element need to have have probability_inputs_arrow as id and not probability_arrow,
The style.display is set to "" by default when the page loads for the first time, thus it ends up in the else block and then requires a second click to go to the if block. Just add this to your else if statement.
<script>
var x = document.getElementById("myDIV");
if (x.style.display == "none")
{
x.style.display = "block";
}
else if((x.style.display == 'null')||(x.style.display == ""))
{
x.style.display = "block";
}
else
{
x.style.display = "none";
}
</script>
I had this problem and it was due to the default state of the DIV. Even if you set the DIV class value is set display: none, it appears some browser do not accept this state, and therefore it is unknown.
To solve the problem, using your own code, add another condition of default:
var toggleDiv = function(id){
var tag = document.getElementById(id).style;
if(tag.display == 'none')
{
document.getElementById(id).style.display='block';
document.getElementById(id + '_arrow').className='arrow_down';
}
else if(tag.display == 'block')
{
document.getElementById(id).style.display='none';
document.getElementById(id + '_arrow').className='arrow_right';
}
else
{
document.getElementById(id).style.display='block';
document.getElementById(id + '_arrow').className='arrow_down';
}
};
`

Categories