I am creating a list of profiles which will be displayed based on a given category. The setup makes it inconvenient to use a container element to wrap the list items, so I'm using display:inline-flex on each item instead of a flex container with the usual justify-this and align-that.
The issue is that the first element in the row appears to have a space to the right of it, and I'm not sure why.
I'd like to display all the elements evenly, in this case 4 to a row with identical spacing, without nesting them in a parent container if possible.
// simple function to repeat html elements
$(document).ready(function() {
let a = $('.a')[0];
const repeats = 11;
let count = 0;
while (count < repeats) {
$('body').append($(a).clone())
count++;
}
//$( 'body' ).append( html );
});
.a {
background-color: red;
box-sizing: border-box;
border: 1px solid green;
display: inline-flex;
height: 25px;
width: 25%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<html>
<body>
<div class="a"></div>
</body>
</html>
Actually now the first element (with space to the right) is one you declared in your html. Remove it from there and use instead:
// simple function to repeat html elements
$(document).ready(function() {
let a = $('<div class="a"></div>');
const repeats = 12;
let count = 0;
while (count < repeats) {
$('body').append($(a).clone())
count++;
}
//$( 'body' ).append( html );
});
.a {
background-color: red;
box-sizing: border-box;
border: 1px solid green;
display: inline-flex;
height: 25px;
width: 25%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<html>
<body>
</body>
</html>
A look at the dev tools inspector reveals a bit of invisible code between the first and second items:
When I delete those lines in the inspector, the gap is removed and all boxes line up as intended.
So it's an issue with your script appending elements. I'm not sure how you want to handle that (e.g., is the script only for this demo? is the problem only in this IDE? is removing the first element an option?), so I won't get into solutions.
Related
I have several HTML elements that I need to display a tooltip on hover. These are not conventional HTML elements and come from a generated script on the backend, which I do not have permissions to alter. What I want to know, from a front end perspective, is how I can display a tooltip without declaring this in the HTML.
I tried using Bootstrap tooltips, but you need to declare this in the HTML tag as a title, so it's not useful. So, as the example shows below, I need some text saying 'Action' to appear in a tooltip when you hover over the 'Action' element that contains 'should'. Same will be applied when you hover over the text 'approximate number of' contained in the 'Quantifier' element - the word 'Quantifier' should be displayed. Hope this makes sense.
<body>
One string that <Action>should</Action> work is
<Quantifier>approximate number of</Quantifier> other things.
<script>
$(document).ready(function(){
$("Action").hover(function(){
});
$("Quantifier").hover(function(){
});
});
</script>
<body>
So far non-conclusive, as I can only change CSS values and not tooltip text.
You can try updating the title property on those elements. One thing to note is that HTML tags will appear in lowercase when compiled.
$(document).ready(function() {
var style = document.createElement('style');
style.type = 'text/css';
$('head')[0].appendChild(style);
style.innerHTML =
`action, quantifier {
position: relative;
display: inline-block;
margin-top: 20px;
}
action[title]:hover:after, quantifier[title]:hover:after {
content: attr(title);
position: absolute;
top: -100%;
left: 0;
}
action[title]:hover:after {
color: red;
border: solid 1px black;
}
quantifier[title]:hover:after {
color: blue;
border: solid 1px black;
}`;
$('action')[0].title = 'Action';
$('quantifier')[0].title = 'Quantifier';
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<body>
One string that <Action>should</Action> work is
<Quantifier>approximate number of</Quantifier> other things.
</body>
add a tooltip for an tag with JS/jQuery without change the html structure. You can modify the css based on requirement.
jQuery(function($){
//If you are able to add class then use $('.add_tooltip').hover
// use $('Quantifier, Action').hover
$('Quantifier, Action').hover(
function () {
//let text = $(this).html(); //this is for html content of hover element
let text = $(this).prop("tagName");
//Add the tag name of hover element to tooltip div
$(this).append('<div class = "tooltip">'+text+'</div>');
//display the tooltip with animation.
$(this).find('.tooltip').hide().fadeIn('slow');
},
//On hover out remove the tooltip.
function () {
$(this).find('.tooltip').remove();
}
);
});
Quantifier, Action{
cursor: pointer;
position:relative;
}
.tooltip{
display: inherit;
background: black;
margin: auto;
padding: 10px;
position: absolute;
z-index: 1000;
width: 200px;
height: 40px;
color: #fff;
top: 18px;
left:10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.0/jquery.min.js"></script>
One string that <Action>should</Action> work is
<Quantifier>approximate number of</Quantifier> other things.
This code currently works, and when each div is clicked the background color and font size will change. In addition, the formatting for one of the other two divs which was already clicked will be removed. The problem is that this will end up requiring a lot of code, what I imagine is far more than is needed. I'm wondering how to repeat less. It is not such a big deal in this example, with only three divs, but my actual project will need many, many more.
I tried including multiple divs, so it would look like this;
document.querySelector(".div2, .div1").classList.remove("styles");
but that did not seem to work.
const div1 = document.querySelector(".div1");
const div2 = document.querySelector(".div2");
const div3 = document.querySelector(".div3");
function makeBigDiv1 () {
document.querySelector(".div1").classList.add("styles");
document.querySelector(".div2").classList.remove("styles");
document.querySelector(".div3").classList.remove("styles");
}
div1.addEventListener("click", makeBigDiv1);
function makeBigDiv2 () {
document.querySelector(".div2").classList.add("styles");
document.querySelector(".div1").classList.remove("styles");
document.querySelector(".div3").classList.remove("styles");
}
div2.addEventListener("click", makeBigDiv2);
function makeBigDiv3 () {
document.querySelector(".div3").classList.add("styles");
document.querySelector(".div1").classList.remove("styles");
document.querySelector(".div2").classList.remove("styles");
}
div3.addEventListener("click", makeBigDiv3);
.div1 {
width:500px;
height:100px;
border: 2px solid black;
}
.div2 {
width:500px;
height:100px;
border: 2px solid black;
}
.div3 {
width:500px;
height:100px;
border: 2px solid black;
}
.styles {
font-size: 50px;
background-color: grey;
}
<div class="div1">One</div>
<div class="div2">Two</div>
<div class="div3">Three</div>
Well as I mentioned the code works, but would just become prohibitively verbose I feel if applied to a large project. I'm relatively new to this and want to write DRY - don't repeat yourself - code. Thanks!
If you want the three divs to have the shared style, you can style them all at once. You can also make a lot of your click functionality reusable. This is what I would do:
const elements = document.querySelectorAll("div")
function attachClickHandler(className) {
return () => {
document.querySelector(`.${className}`).classList.add('styles');
document.querySelectorAll(`div:not(.${className})`).forEach(element => { element.classList.remove('styles') });
}
}
elements.forEach(element => {
element.addEventListener("click", attachClickHandler(element.className))
})
<html>
<head>
<style>
.div1, .div2, .div3 {
width:500px;
height:100px;
border: 2px solid black;
}
.styles {
font-size: 50px;
background-color: grey;
}
</style>
</head>
<body>
<div class="div1">One</div>
<div class="div2">Two</div>
<div class="div3">Three</div>
<script>
</script>
</body>
</html>
In context you would probably not want to add event listeners to every div so you could just add a class to all divs you want to make selectable and find all by class name instead of find all of type div. This would also allow you to add the base styling for the shared class instead of to all 3 divs.
You can do this quite easily just by looping thru the divs. here is an example. There is some optimization you can do but you get the idea
const div = document.querySelectorAll('.div');
for (var i = 0; i < div.length; i++) {
div[i].addEventListener('click', function(event) {
for (var j = 0; j < div.length; j++) {
// remove styles class from all the div classes
div[j].classList.remove("styles");
}
// add styles class only to the clicked item
this.classList.add("styles");
});
}
.styles {
font-size: 50px;
background-color: grey;
}
<div class="div">One</div>
<div class="div">Two</div>
<div class="div">Three</div>
this.classList.add("styles"); The this refers to the clicked item
Here are some changes:
Use the same className for each block, and give it a specific name (i.e. box).
Same for the added className, make it clear. (i.e. is-selected).
Don't duplicate functions for the same action and use forEach instead to loop through each box.
// Get all boxes
const boxes = document.querySelectorAll('.box');
// For each box
[...boxes].forEach(box => {
// Attach an event click listener
box.addEventListener('click', () => {
// Add the `is-selected` className to the clicked one
box.classList.add('is-selected');
// Remove the `is-selected` className to all the others
[...boxes].filter(el => el !== box).forEach(box => {
box.classList.remove('is-selected');
})
});
});
.box {
width: 500px;
height: 100px;
border: 2px solid black;
}
.box.is-selected {
font-size: 50px;
background-color: grey;
}
<div class="box">One</div>
<div class="box">Two</div>
<div class="box">Three</div>
I've got a simple text button with an image of an arrow next to it. I'm wanting the arrow image to move when someone hovers over the button.
I currently have this working in one instance with JS 'document.getElementById...', but I have several buttons across my site that I'd like to have the same behavior. My first thought would be to use a class instead of an id, and use the same functions.
For whatever reason, document.getElementsByClassName doesn't work - even in one instance.
Here's a simpler version to demonstrate - View on Codepen: https://codepen.io/sdorr/pen/JxYNpg
HTML
<HTML>
hover over me
<div id="block"></div>
hover over me
<div class="block"></div>
CSS
* {
margin: 0;
padding: 0;
}
.button {
color: #000000;
text-decoration: none;
background-color: cyan;
margin: 0;
display: block;
width: 300px;
padding: 20px;
text-align: center;
}
#block {
width: 30px;
height: 30px;
background-color: red;
}
.block {
width: 30px;
height: 30px;
background-color: green;
}
JS
function move() {
document.getElementById("block").style.marginLeft = "35px";
}
function moveBack() {
document.getElementById("block").style.marginLeft = "0px";
}
function moveAlt() {
document.getElementsByClassName("block").style.marginLeft =
"35px";
}
function moveBackAlt() {
document.getElementsByClassName("block").style.marginLeft =
"0px";
}
First off, why isn't the behavior with a class working but an id works fine?
Secondly, would a class solve this issue and be scalable across all buttons with the same two functions (onmouseover / onmouseout)?
If not, any ideas on a solution? I currently have a solution I found using jQuery that does work, but when hovering over one button, all arrow images move across the site. I don't necessarily mind this behavior because only one button is really in view at a time - but I'm trying to learn JS and solve problems with my own solutions!
I greatly appreciate your desire to learn on your own and not rely on premade solutions. Keep that spirit and you will go places!
When it comes to getElementsById, we know this should work for one element, since the function returns a single Element.
However, what does getElementsByClassName return?
(see: https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementsByClassName)
It returns an HTMLCollection which you can iterate over to change an single element's style.
So, to get this to work with JavaScript you need to write a function that will be able to identify the particular div.block you want to move. But, this puts you back to where you started, needing some particular identifier, like an id or a dataset value to pass to the function.
Alternately, based on the HTML structure you provide, you could look for nextElementSibling on the a that get's clicked. But I would set up an eventListener rather than adding a JS function as a value to the onmouseenter property.
const btns = document.getElementsByTagName('a');
/*** UPDATE forEach is a NodeList method, and will fail on HTMLCollection ***/
/* this fails -> Sorry! ~~btns.forEach(button=>{~~
/* the following will work
/**********/
for (let i = 0; i < btns.length; i++){
btns[i].addEventListener('mouseenter', function(e) {
//we pass e to the function to get the event and to be able to access this
const block = this.nextElementSibling;
block.style.marginLeft = "35px";
})
btns[i].addEventListener('mouseleave', function(e) {
const block = this.nextElementSibling;
block.style.marginLeft = "0";
})
}
But with siblings, there is a CSS-only solution.
We can use the Adjacent Sibling Selector combined with the :hover state selector and no JavaScript is needed, if we are just moving back and forth.
.button:hover+.block {
margin-left: 35px;
}
See the Snipped Below
* {
margin: 0;
padding: 0;
}
.button {
color: #000000;
text-decoration: none;
background-color: cyan;
margin: 0;
display: block;
width: 300px;
padding: 20px;
text-align: center;
}
.block {
width: 30px;
height: 30px;
background-color: green;
}
.button:hover+.block {
margin-left: 35px;
}
hover over me
<div class="block"></div>
hover over me
<div class="block"></div>
As Vecta mentioned, getElementsByClassName returns an array-like. You'll need to do something like this to get the first element:
function moveAlt() {
document.getElementsByClassName("block")[0].style.marginLeft = "35px";
}
function moveBackAlt() {
document.getElementsByClassName("block")[0].style.marginLeft = "0px";
}
However a better solution might be to use document.querySelector, which operates similarly to jQuery's $() syntax:
function moveAlt() {
document.querySelector(".block").style.marginLeft = "35px";
}
function moveBackAlt() {
document.querySelector(".block").style.marginLeft = "0px";
}
I am trying to change content in the div, when user typing in other div. Everything works fine: JSFiddle
But when I insert these divs inside another div (I insert these divs inside HTML-editor that is editable div) - it is not working at all.
How can it block this?
$(document).keyup(function() {
var add_title = $("#add_title").html();
$("#title").html(add_title);
});
* {
box-sizing: border-box;
}
.input {
display: inline-block;
min-width: 400px;
min-height: 40px;
padding: 10px;
border: 1px solid blue;
}
.title {
margin-bottom: 10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id=title class=title>default</div>
<div id=add_title class=input contenteditable=true></div>
Try this.
$(document).on('keyup','.input',function () {
var add_title = $(this).html();
$( "#title" ).html(add_title);
});
It seems like you are adding dynamic elements to the page and for those elements it is not working. if that is the issue then you have to look into event delegation a bit more. The above code will hopefully fix your issue.
I have a wrapper div and many content blocks. The content block can be of any number.
<div class="wrapper">
<div class="content-block">Something goes here</div>
<div class="content-block">Something goes here</div>
.
.
.
<div class="content-block">Something goes here</div>
</div>
I wish to form a pyramid structure using these content-blocks as it appears below:
Is it possible to achieve pyramid like this? The above image is just an example, there can be more than 10 content-blocks or even less.
Check out this very simple JavaScript/CSS solution:
var objContainer = document.getElementById("container"),
intLevels = 10,
strBlocksHTML = '';
// Using innerHTML is faster than DOM appendChild
for (var i = 0; i < intLevels; i++) {
for (var n = 0; n < i + 1; n++) {
strBlocksHTML += '<div class="buildingBlock"></div>';
}
strBlocksHTML += '<div></div>'; // Line break after each row
}
objContainer.innerHTML = strBlocksHTML;
.buildingBlock {
display: inline-block;
width: 20px;
height: 20px;
margin: 2px 5px;
background-color: #eee;
border: 2px solid #ccc;
}
#container {
text-align: center;
}
<div id="container"></div>
Yes, it is perfectly possible, but hard to write down without more precise requirements. Number of divs would obviously equal number of elements = 10. Length of bottom row = (10/2 - 1) with each next row to top taking one less element, etc. Either use absolute positioning in div style or treat table as matrix and draw with cells. Table solution will be progressively slower with more rows, because all the empty "pixels" and quadratically increasing overhead on recalculating cell sizes and positions in browser.
Hm, not a trivial task. I don't think it is possible to write (finite) CSS for any number of elements. It would need something like this:
#wrapper {
text-align: center;
}
.content-block {
display: inline-block;
width: 5em;
height: 4em;
margin: 0 2.5em;
}
.content-block:nth-child(n*(n+1)/2)::after {
display: block; /* linebreak */
}
Where the nth-child-selector would contain a triangular number, but it must have the form an+b.