In HTML nested links are not permitted. However, for my purpose (text notes which sometimes refer to whole sentences and sometimes to just one single word within already anotated sentences) I need them. So I have to find a way to solve this problem.
However, all I have now is a basic idea on how it should look and behave. The following mock up shows two links: one to target A, one to B. The "outer" a link is, the lower is the line under it. A is the outer link, thus, its line is lower than that of B. Clicking on the lines of a link should always lead to the target of that link - even if the text above that line is the text of an inner link.
I've tried to show that intended behaviour with hover colors: Blue for A, pink for B.
Any ideas how I could realize this in HTML with the help of CSS (and maybe SVG?). I'd prefer solutions without scripting, but any suggestions are welcomed.
You can use <span>s inside links:
a {color: #00f; border-bottom: 1px solid; text-decoration: none;}
a span {color: #66f; text-decoration: underline;}
Hello, this is link. This is <span>inner link</span> here.
A small problem or extra work is, you need JavaScript to make them follow the links.
But as you asked, you can get the UI Effect without any scripting, but the following of link, definitely needs scripting!
Expanding on the answer from #connexo, you can wrap them all in a span and use a border-bottom on that.
.split-link {
border-bottom:1px solid blue;
padding-bottom:1px; /* for visual reference only */
}
.split-link a {
text-decoration: none;
}
.split-link a.inner-link {
text-decoration: underline;
text-decoration: red;
color:red;
}
<span class="split-link">
Hello, this is a link. It has an
inner link
here.
</span>
You will want to stay within valid HTML, so your only chance (aside of JS) is splitting the outer link into two links.
Hello, this is link. This is inner link here.
.inner-link { color: #66f; text-decoration: underline; }
This will split the blue line in your example into two parts as well, which I assume you do not want. But it's not possible otherwise.
Use JavaScript for best results
I know:
I'd prefer solutions without scripting,
but…
any suggestions are welcomed.
You can add an inline onclick handler to a child span:
AAAA <span onclick="event.preventDefault(); window.location.assign('#B'); return false;">BBBB</span> AAAA
Or, to be DRY, pass in a reference to the handler instead:
AAAA <span onclick="embedLink('#B');">BBBB</span> AAAA
Definition of handler:
function embedLink(url) {
event.preventDefault();
window.location.assign(url);
return false;
}
Working example:
a {
display: inline-block;
text-decoration: none;
color: blue;
border-bottom: 1px solid blue;
padding: 1px;
}
a .annotation {
color: fuchsia;
border-bottom: 1px double fuchsia;
background-color: white;
}
a:hover {
background-color: lightblue;
}
a .annotation:hover {
background-color: lightpink;
}
AAAA <span data-href="#B" class="annotation" onclick="event.preventDefault(); window.location.assign(this.getAttribute('data-href')); return false;">BBBB</span> AAAA
With JS, you can handle other possibilities as well:
Open in new window. Use: window.open() instead of window.location.assign().
Copy to clipboard. Add an event listener to the context and copy events on the parent link. In the handler, use document.execCommand('copy') to grab the url from the clicked child span instead; perhaps its URL is stored in a data-href attribute.
Display URL in status bar. Add a mouseover event listener. In the handler, set window.status = url.
Thank your all for your answers! They all have inspired me!
After some hard thinking and merging your answers together I came to the following solution whose greatest advantage is that the basic functions of all links work without JavaScript.
My main idea is to wrap all links inside a <span> element and, as connexo has suggested, to split up those links which contain links themself. Thus, the HTML skeleton of my above example looks like this:
<span>
<a>AAA</a>
<span><a>BBB</a></span>
<a>AAA</a>
</span>
All JavaScript is associated just with the <span>. Onmouseover, it removes the hover-class from all ancestor <span>. Onclick, it takes the url of the first link child and redirects there.
The CSS is rather simple. It removes the underline from links and defines just how the span should look like (and behave in case of hover).
Another advantage of this design is that nested nested links are also supported, as you can see in the snippet below.
function link_span_click(current_element,current_event)
{
current_event.preventDefault();
current_event.stopPropagation();
var target_href = current_element.getElementsByTagName('a')[0].href;
window.location.assign(target_href);
}
function link_span_mouse_over(current_element)
{
while (current_element)
{
current_element.parentNode.classList.remove('link_span_hover');
current_element = current_element.parentNode;
}
var target_href = current_element.getElementsByTagName('a')[0].href;
window.status = target_href;
}
function link_span_mouse_out(current_element)
{
while (current_element)
{
current_element.parentNode.classList.add('link_span_hover');
current_element = current_element.parentNode;
}
window.status = '';
}
a.nested_link {
text-decoration: none;
}
span.link_span {
cursor: pointer;
display: inline-block;
padding-bottom: 3px;
background-color: white;
border-bottom: 1px solid blue;
}
span.link_span_hover:hover {
background-color: lightblue;
}
<div>
<span
class="link_span link_span_hover"
onclick="link_span_click(this, event)"
onmouseout="link_span_mouse_out(this)"
onmouseover="link_span_mouse_over(this)"
>
AAA
<span
class="link_span link_span_hover"
onclick="link_span_click(this, event)"
onmouseout="link_span_mouse_out(this)"
onmouseover="link_span_mouse_over(this)">
BBB
</span>
AAA
</span>
</div>
<div>
<span
class="link_span link_span_hover"
onclick="link_span_click(this,event)"
onmouseout="link_span_mouse_out (this)"
onmouseover="link_span_mouse_over (this)">
AAA AAA AAA AAA
<span
class="link_span link_span_hover"
onclick="link_span_click(this, event)"
onmouseout="link_span_mouse_out(this)"
onmouseover="link_span_mouse_over(this)">
BBB BBB
<span
class="link_span link_span_hover"
onclick="link_span_click(this, event)"
onmouseout="link_span_mouse_out(this)"
onmouseover="link_span_mouse_over(this)">
CCC
</span>
BBB BBB
</span>
AAA AAA AAA AAA
</span>
</div>
Still, there remains one problem: If a rather long textlink gets split up into to lines only the second (or last to be precise) line gets underlined.
Related
I was trying to set a background image for my PicInner css class but it wasn't working, so I'm trying to just even change the background color:
function changeBackground()
{
$('.PicInner').css('background', 'orange');
}
I've tried it without the . for the class selector but that doesn't work either. What could be overriding this?
I know the function is being called because I inserted an Alert("Hello!"); line and it pops up every time I refresh the page, but the background colour of my .PicInner class just doesn't change. I've changed the background in the F12 editor on my PicInner class and it works fine, just the javascript doesn't seem to be changing anything for me.
Where could I be going wrong?
The relevant html section is
<script>
changeBackground();
function changeBackground()
{
alert("HELLO!");
('.PicInner').css('background', 'orange');
}
</script>
<link rel="stylesheet" type="text/css" href="~/css/CJBStyles.css">
<div class="wContainer">
<div class="cjb-bkg">
<div class="PicInner">
<h3>Bespoke Kitchens, bathrooms and fittings</h3>
<h3>Prestigious and high quality</h3>
</div>
</div>
</div>
and the css for PicInner is:
.PicInner{
background: rgba(255,255,255,0.8);
padding: 10px;
text-align:center;
position: relative;
}
Here is the function fired on "Page loaded" event:
function changeBackground() {
var elm = document.querySelector('.PicInner');
elm.style.backgroundColor = 'orange'
}
changeBackground();
.PicInner {
background: rgba(255, 255, 255, 0.8);
padding: 10px;
text-align: center;
position: relative;
}
<div class="wContainer">
<div class="cjb-bkg">
<div class="PicInner">
<h3>Bespoke Kitchens, bathrooms and fittings</h3>
</div>
</div>
</div>
There can not be tag in the snippet, but I will let you know that, in a "real" web page, you can explicitly fire changeBackground() on page load:
<body onload="changeBackground()">
Ok, your main problem seems to stem from the confusion about what $('.PicInner') does. This expression does NOT return the CSS class itself. Rather, it searches for all the HTML elements that have this class - at that point in time - and returns a collection (something similar to an array, but with extra methods) of those. And then when you do .css('background', 'orange') you're actually setting the style property on each of them individually. You're NOT modifying the CSS class itself.
With that in mind, the reason why your code doesn't do anything becomes obvious - the document is loaded and code is executed in sequence. At the time when you're executing this code, the HTML element with class="PicInner" isn't yet loaded. So nothing happens.
If you use jQuery selectors, you must include the jQuery library. Else, you can use plain JavaScript (and I advise you to do so).
Also, you have to run your function, on an event (I see you have edited your question and are now running your function onload):
function changeBackground() {
var elm = document.querySelector('.PicInner');
elm.style.backgroundColor = 'orange'
}
.PicInner {
background: rgba(255, 255, 255, 0.8);
padding: 10px;
text-align: center;
position: relative;
}
<div class="wContainer">
<div class="cjb-bkg">
<div class="PicInner">
<h3>Bespoke Kitchens, bathrooms and fittings</h3>
</div>
</div>
</div>
<button onclick="changeBackground()">Run function!!!</button>
Above, your function is run ("fired") upon clicking a button.
I am trying to use JavaScript to change the background color of an element after being selected, and also to make sure that only one element at a time has the particular background color. Once the user selects on a different element I would like the previous element that was selected to be replaced by a different background color. Currently I am only able to toggle individual elements by selecting on EACH element. I need to be able to select on an element and apply the new background color, then have JavaScript change the background color of the previously active element to a different color (one less click).
What I am trying to do is very similar to modern navbars or list items where only one element at a time is “active” and has a background color that is different than the other elements in the same div, row, etc.
Notes about my work I am utilizing bootstrap and have no desire to use jQuery for this particular project.
CSS:
<!DOCTYPE html>
<html lang="en">
<head>
<style>
h4 {
border: 1px solid black;
border-radius: 8px;
padding: 10px 2px 10px 2px;
margin: 20px 20px 0px 20px;
background-color: #F0F0F0;
border-color: #F8F8F8;
color: #505050;
cursor: pointer;
}
.active {
background-color: #99E6FF;
}
</style>
</head>
</html>
HTML:
<div id="pTwoRowOne">
<div class="row">
<div class="col-md-4 row row-centered">
<h4 id="techBio" class="test">Biology</h4>
</div>
<div class="col-md-4 row row-centered">
<h4 id="techCart" class="test">Cartography</h4>
</div>
<div class="col-md-4 row row-centered">
<h4 id="techChem" class="test">Chemistry</h4>
</div>
</div>
</div>
JavaScript:
document.getElementById("techBio").onclick=function() {
document.getElementById("techBio").classList.toggle('active');
}
document.getElementById("techCart").onclick=function() {
document.getElementById("techCart").classList.toggle('active');
}
document.getElementById("techChem").onclick=function() {
document.getElementById("techChem").classList.toggle('active');
}
An example can be seen here: http://jsbin.com/fugogarove/1/edit?html,css,js,output
If clarification is needed let me know.
Yup, pretty straightforward.
Assumptions
You're not trying to support IE8, since you're using classList
You're okay with housing your elements as variables as opposed to repeatedly querying the DOM.
Example
JSBin
Code
I rewrote your JavaScript to make it a little bit cleaner and to DRY it up a bit:
var techs = [].slice.call(document.querySelectorAll('#pTwoRowOne h4'));
function set_active(event) {
techs.forEach(function(tech){
if (event.target == tech) { return; }
tech.classList.remove('active');
});
event.target.classList.toggle('active');
}
techs.forEach(function(item) {
item.addEventListener('click', set_active);
});
Some explanation
[].slice.call(document.querySelectorAll('#pTwoRowOne h4')); – We're using this to change the output from a NodeList to an Array. This allows us to use forEach later. querySelectorAll returns a NodeList that contains all elements matching the CSS selector. You can probably replace that with a better CSS selector depending on your environment.
addEventListener is a much nicer way than the iterative add via onclick += to bind an event listener. It's also the recommended way (as far as I know) in ECMA5 and later.
By setting the element queries as variables, you'll be able to keep the reference in memory instead of polling the DOM every time to alter elements. That'll make your JavaScript marginally faster, and it's again just a nicer, cleaner version of the code which it produces.
updates
I reworked the JS to make more sense.
Assuming you only ever have one active element, you can find it using document.querySelector() - if you can have multiples you can use document.querySelectorAll() and iterate through them.
Simple case:
function activate(event) {
var active=document.querySelector('.active');
// activate the clicked element (even if it was already active)
event.target.classList.add('active');
// deactivate the previously-active element (even if it was the clicked one => toggle)
if (active) active.classList.remove('active');
}
document.getElementById("techBio").addEventListener("click",activate);
document.getElementById("techCart").addEventListener("click",activate);
document.getElementById("techChem").addEventListener("click",activate);
h4 {
border: 1px solid black;
border-radius: 8px;
padding: 10px 2px 10px 2px;
margin: 20px 20px 0px 20px;
background-color: #F0F0F0;
border-color: #F8F8F8;
color: #505050;
cursor: pointer;
}
.active {
background-color: #99E6FF;
}
<div id="pTwoRowOne">
<div class="row">
<div class="col-md-4 row row-centered">
<h4 id="techBio" class="test">Biology</h4>
</div>
<div class="col-md-4 row row-centered">
<h4 id="techCart" class="test">Cartography</h4>
</div>
<div class="col-md-4 row row-centered">
<h4 id="techChem" class="test">Chemistry</h4>
</div>
</div>
</div>
Another similar yet simpler way to do it: jsBin ;)
var H4 = document.getElementsByClassName("test"), act;
[].forEach.call(H4, function(el){
el.addEventListener("click", function(){
if(act) act.classList.remove("active");
return (this.classList.toggle("active"), act=this);
});
});
You can do something like this:
[].slice.call(document.querySelectorAll(".test")).forEach(function(element) {
element.addEventListener('click', function(event) {
if (activeElement = document.querySelector(".test.active")) {
activeElement.classList.remove("active");
};
event.target.classList.add('active');
});
});
Basically, first we remove the active class from the active element, then we add it to the target.
JSBin
I am trying to have the classes change depending on what is clicked from the two headings.
If heading one is clicked, I want the font color to change to red and have it underlined with red, which in the class it currently does with a bottom border. If the other heading is clicked then I want that heading to take on the red characteristics. The one that is not clicked will just stay grey according to the no highlight class.
Here is the JSFiddle: http://jsfiddle.net/7ok991am/1/ I give an example look also of what I am trying to accomplish.
HTML:
<div id="page_headings">
<h2 class="no_highlight">Heading One</h2>
<h2 class="no_highlight">Heading Two</h2>
</div>
CSS:
#page_headings{
overflow: hidden;
margin-bottom: 32px;
}
#page_headings h2{
float: left;
margin-right:24px;
font-size: 14px;
cursor:pointer;
}
#page_headings h2:hover{
font-weight: bold;
}
.red_highlight{
color:red;
border-bottom: 1px solid red;
}
.no_highlight{
color:#898989;
}
JS:
$('#page_headings').on('click', function(){
if($('#page_headings h2').hasClass('no_highlight')){
$(this).removeClass('no_highlight').addClass('red_highlight');
}else{
$('#page_headings h2').addClass('no_highlight');
};
});
Building on #RDrazard I think you want them to switch between the two correct?
http://jsfiddle.net/7ok991am/3/
$('#page_headings h2').on('click', function(){
if($(this).hasClass('no_highlight')){
$(this).removeClass('no_highlight').addClass('red_highlight');
}else{
$(this).addClass('no_highlight');
}
$(this).siblings('h2').addClass('no_highlight');
});
JSFiddle: Link
First off, add a border-bottom property with none to the no highlight class to ensure that it looks just the same before the click.
Next, you want to the click event associated with the h2 elements, so it should be $('#page_headings h2')
Use this to impact the h2 we're clicking on.
Try this code
$('#page_headings h2').on('click', function(){
if($(this).hasClass('no_highlight')){
$(this).removeClass('no_highlight').addClass('red_highlight');
}else{
$(this).addClass('no_highlight').removeClass('red_highlight');
}
});
Check this fiddle
JS
$('.no_highlight').on('click', function(){
$(this).parent().find('.no_highlight').css('border-bottom','none');
$(this).css('border-bottom','1px solid red');
});
The above method changes the border of the currently clicked headinh which i think is what you want.
AND
if you want addClass() and removeClass(), then see this fiddle
JS
$('.no_highlight').on('click', function(){
$(this).parent().find('.no_highlight').removeClass('red_highlight');
$(this).addClass('red_highlight');
});
This method adds a red_highlight class to the active link and removes the red_highlight when not active.
Please try it..
Here is the HTML
<a style="border: medium none; display: block;" class="agendaNav" href="#"><img class="rightArrow" src="/images/arrowdown.png"> WEEK AT A GLANCE</a>
<div class="agendaDay">
Content In Here
</div>
The Same link / class is repeated several times and I use the following JQuery:
$('.agendaNav').click(function(event){
event.preventDefault();
if($(this).find('.rightArrow').attr('src')=='/images/arrowdown.png'){
$(this).find('.rightArrow').attr('src', '/images/arrowright.png');
$(this).attr('style', 'border-bottom: 1px solid #fff; display: block;');
} else {
$(this).find('.rightArrow').attr('src', '/images/arrowdown.png');
$(this).attr('style', 'border: none; display: block;');
}
$('.agendaDay').toggle('fast');
});
If you take a look here:
http://icuc2011.com/agenda
You'll see that it changes every class of agendaDay which makes sense, but I only want to change the one directly after the 'a' tag that was clicked, I tried this:
$(this).next('.agendaDay').toggle('fast');
But then the arrow changes but the div of class agenda day doesn't change at all, what am I doing wrong?
Your example doesn't match your actual code. In your code the link is inside a p tag. The div is a sibling following this p tag. If this is consistently the way it works, then you want:
$(this).parent().next('.agendaDay').toggle();
I would like to move one DIV element inside another. For example, I want to move this (including all children):
<div id="source">
...
</div>
into this:
<div id="destination">
...
</div>
so that I have this:
<div id="destination">
<div id="source">
...
</div>
</div>
You may want to use the appendTo function (which adds to the end of the element):
$("#source").appendTo("#destination");
Alternatively you could use the prependTo function (which adds to the beginning of the element):
$("#source").prependTo("#destination");
Example:
$("#appendTo").click(function() {
$("#moveMeIntoMain").appendTo($("#main"));
});
$("#prependTo").click(function() {
$("#moveMeIntoMain").prependTo($("#main"));
});
#main {
border: 2px solid blue;
min-height: 100px;
}
.moveMeIntoMain {
border: 1px solid red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="main">main</div>
<div id="moveMeIntoMain" class="moveMeIntoMain">move me to main</div>
<button id="appendTo">appendTo main</button>
<button id="prependTo">prependTo main</button>
My solution:
Move:
jQuery("#NodesToMove").detach().appendTo('#DestinationContainerNode')
copy:
jQuery("#NodesToMove").appendTo('#DestinationContainerNode')
Note the usage of .detach(). When copying, be careful that you are not duplicating IDs.
Use a vanilla JavaScript solution:
// Declare a fragment:
var fragment = document.createDocumentFragment();
// Append desired element to the fragment:
fragment.appendChild(document.getElementById('source'));
// Append fragment to desired element:
document.getElementById('destination').appendChild(fragment);
Check it out.
Try plain JavaScript: destination.appendChild(source);.
onclick = function(){ destination.appendChild(source) };
div {
margin: .1em;
}
#destination {
border: solid 1px red;
}
#source {
border: solid 1px gray;
}
<div id=destination>
###
</div>
<div id=source>
***
</div>
I just used:
$('#source').prependTo('#destination');
Which I grabbed from here.
If the div where you want to put your element has content inside, and you want the element to show after the main content:
$("#destination").append($("#source"));
If the div where you want to put your element has content inside, and you want to show the element before the main content:
$("#destination").prepend($("#source"));
If the div where you want to put your element is empty, or you want to replace it entirely:
$("#element").html('<div id="source">...</div>');
If you want to duplicate an element before any of the above:
$("#destination").append($("#source").clone());
// etc.
You can use:
To insert after,
jQuery("#source").insertAfter("#destination");
To insert inside another element,
jQuery("#source").appendTo("#destination");
You can use the following code to move the source to the destination:
jQuery("#source")
.detach()
.appendTo('#destination');
Try the working CodePen.
function move() {
jQuery("#source")
.detach()
.appendTo('#destination');
}
#source{
background-color: red;
color: #ffffff;
display: inline-block;
padding: 35px;
}
#destination{
background-color:blue;
color: #ffffff;
display: inline-block;
padding: 50px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="source">
I am source
</div>
<div id="destination">
I am destination
</div>
<button onclick="move();">Move</button>
If you want a quick demo and more details about how you move elements, try this link:
http://html-tuts.com/move-div-in-another-div-with-jquery
Here is a short example:
To move ABOVE an element:
$('.whatToMove').insertBefore('.whereToMove');
To move AFTER an element:
$('.whatToMove').insertAfter('.whereToMove');
To move inside an element, ABOVE ALL elements inside that container:
$('.whatToMove').prependTo('.whereToMove');
To move inside an element, AFTER ALL elements inside that container:
$('.whatToMove').appendTo('.whereToMove');
I need to move content from one container to another including all the event listeners. jQuery doesn't have a way to do it, but the standard DOM function appendChild does.
// Assuming only one .source and one .target
$('.source').on('click',function(){console.log('I am clicked');});
$('.target')[0].appendChild($('.source')[0]);
Using appendChild removes the .source* and places it into target including its event listeners: Node.appendChild() (MDN)
You may also try:
$("#destination").html($("#source"))
But this will completely overwrite anything you have in #destination.
You can use pure JavaScript, using appendChild() method...
The appendChild() method appends a node as the last child of a node.
Tip: If you want to create a new paragraph, with text, remember to
create the text as a Text node which you append to the paragraph, then
append the paragraph to the document.
You can also use this method to move an element from one element to
another.
Tip: Use the insertBefore() method to insert a new child node before a
specified, existing, child node.
So you can do that to do the job, this is what I created for you, using appendChild(), run and see how it works for your case:
function appendIt() {
var source = document.getElementById("source");
document.getElementById("destination").appendChild(source);
}
#source {
color: white;
background: green;
padding: 4px 8px;
}
#destination {
color: white;
background: red;
padding: 4px 8px;
}
button {
margin-top: 20px;
}
<div id="source">
<p>Source</p>
</div>
<div id="destination">
<p>Destination</p>
</div>
<button onclick="appendIt()">Move Element</button>
I noticed huge memory leak & performance difference between insertAfter & after or insertBefore & before .. If you have tons of DOM elements, or you need to use after() or before() inside a MouseMove event, the browser memory will probably increase and next operations will run really slow.
The solution I've just experienced is to use inserBefore instead before() and insertAfter instead after().
Dirty size improvement of Bekim Bacaj's answer:
div { border: 1px solid ; margin: 5px }
<div id="source" onclick="destination.appendChild(this)">click me</div>
<div id="destination" >...</div>
For the sake of completeness, there is another approach wrap() or wrapAll() mentioned in this article. So the OP's question could possibly be solved by this (that is, assuming the <div id="destination" /> does not yet exist, the following approach will create such a wrapper from scratch - the OP was not clear about whether the wrapper already exists or not):
$("#source").wrap('<div id="destination" />')
// or
$(".source").wrapAll('<div id="destination" />')
It sounds promising. However, when I was trying to do $("[id^=row]").wrapAll("<fieldset></fieldset>") on multiple nested structure like this:
<div id="row1">
<label>Name</label>
<input ...>
</div>
It correctly wraps those <div>...</div> and <input>...</input> BUT SOMEHOW LEAVES OUT the <label>...</label>. So I ended up use the explicit $("row1").append("#a_predefined_fieldset") instead. So, YMMV.
The .appendChild does precisely that - basically a cut& paste.
It moves the selected element and all of its child nodes.