1

I have an <img> tag followed by a <span> tag which I am saving in a variable named $contents. However, I want to replace the <span> tag with a a <figcaption> before using add() to add it to the image tag. I can't seem to get it to do that. Here's what I have so far:

First the HTML

<img src="whatever.jpg" />
<span>Copyright Stackoverflow</span>

With this Jquery code:

  $elem.find("img, img + span").each(function(innerIndex) {
    var $contents = $(this).add($(this).next('span'));
    });

What I end up with:

<img src="whatever.jpg" /> <span>Copyright Stackoverflow</span>

What I want to happen is more like this (if it worked which it doesn't):

$elem.find("img, img + span").each(function(innerIndex) {

// first replace the span with a figcaption
var $figcaption = $(this).next('span').unwrap().wrap('<figcaption/>');

// add the new element to the img tag to make the new contents
var $contents = $(this).add($figcaption);
});

So I can end up with this instead:

<img src="whatever.jpg" /> <figcaption>Copyright Stackoverflow</figcaption>

When I output $contents to the page, I get an empty <span> instead of one wrapped in <figcaption> tags. How am I supposed to do this?

UPDATE: To clarify, I need to get the finished HTML into a variable because it gets used in different places later on. So all this <img src="whatever.jpg" /><figcaption>Copyright</figcaption> must be inside a var.

volume one
  • 6,800
  • 13
  • 67
  • 146
  • You want to wrap the `` with a `
    `, or you want to replace the `` with a `
    `? You seem to use both versions in your question.
    – David Thomas Dec 02 '15 at 19:11
  • I want to replace the span with a figcaption - you can see it in the end result that I want but can't achieve. The contents of the span tag itself must remain :) – volume one Dec 02 '15 at 19:23

4 Answers4

3

To turn:

<img src="whatever.jpg" />
<span>Copyright Stackoverflow</span>

Into:

<img src="whatever.jpg" />
<figcaption>Copyright Stackoverflow</figcaption>

I'd suggest:

// selecting the relevant elements,
// using the replaceWith() method to
// replace those found elements:
$('img + span').replaceWith(function(){

  // returning a string comprised of the HTML tags,
  // surrounding the text from the 'this' (the current
  // <span> element of the jQuery collection) node:
  return '<figcaption>' + this.textContent + '</figcaption>'
});

$('img + span').replaceWith(function(i, el) {
  return '<figcaption>' + this.textContent + '</figcaption>'
});
span {
  color: limegreen;
}
figcaption {
  color: #f90;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<img src="whatever.jpg" />
<span>Copyright Stackoverflow</span>

JS Fiddle demo.

Or, in order to retain event-handlers on any child elements:

// a simple function bound to the click event
// on the <span> element within a parent <span>
// element:
$('span > span').click(function () {
  console.log('woo');
})

// finding the relevant <span> elements:
$('img + span').replaceWith(function () {

  // returning a created <figcaption> element,
  // after appending the contents of the
  // found <span> element(s):
  return $('<figcaption>').append($(this).contents());
});

$('span > span').click(function() {
  console.log('woo');
})

$('img + span').replaceWith(function() {
  return $('<figcaption>').append($(this).contents());
});
span {
  color: limegreen;
}
figcaption {
  color: #f90;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<img src="whatever.jpg" />
<span><span>Copyright</span> Stackoverflow</span>

JS Fiddle demo.

Alternatively, in native JavaScript:

// creating a named function, and its arguments:
function replaceWith(original, tag) {

  // tag: String, the element-type to be created,
  // here we remove any '<' or '>' characters, to
  // ensure that '<fieldset>' becomes 'fieldset':
  tag = tag.replace(/<|>/g, '');

  // creating a new element of that type:
  var newEl = document.createElement(tag);

  // setting the innerHTML of the created element
  // that of the original element:
  newEl.innerHTML = original.innerHTML;

  // replacing the original child with the new element:
  original.parentNode.replaceChild(newEl, original);
}

// finding the relevant elements:
var elements = document.querySelectorAll('img + span'),

  // converting the collection of elements into an
  // an Array:
  elementArray = Array.prototype.slice.call(elements, 0);

// iterating over the Array using Array.prototype.forEach():
elementArray.forEach(function (elem) {

  // calling the function, passing in the current array-element
  // of the array over which we're iterating:
  replaceWith(elem, '<figcaption>')
});

function replaceWith(original, tag) {
  tag = tag.replace(/<|>/g, '');
  var newEl = document.createElement(tag);
  newEl.innerHTML = original.innerHTML;

  original.parentNode.replaceChild(newEl, original);
}

var elements = document.querySelectorAll('img + span'),
  elementArray = Array.prototype.slice.call(elements, 0);

elementArray.forEach(function(elem) {
  replaceWith(elem, '<figcaption>')
});
span {
  color: limegreen;
}
figcaption {
  color: #f90;
}
<img src="whatever.jpg" />
<span>Copyright Stackoverflow</span>

JS Fiddle demo.

Further, if you wish to retain event-handlers on child elements – using native JavaScript:

// finding the <span> elements with a <span> parent:
document
  .querySelector('span > span')

  // adding a simple anonymous function as the
  // handler for the click event:
  .addEventListener('click', function () {

    // logging a simple message to the console:
    console.log('woo')
  });

function replaceWith(original, tag) {
  tag = tag.replace(/<|>/g, '');
  var newEl = document.createElement(tag);

  // this is the only change, while the
  // original element contains a firstChild node
  // we append that child-node to the newly
  // created-element:
  while (original.firstChild) {

    // using Node.appendChild to move the firstChild
    // of the original node into the created-element:
    newEl.appendChild(original.firstChild)
  }

  original.parentNode.replaceChild(newEl, original);
}

var elements = document.querySelectorAll('img + span'),
  elementArray = Array.prototype.slice.call(elements, 0);

elementArray.forEach(function (elem) {
  replaceWith(elem, '<figcaption>')
});

document.querySelector('span > span').addEventListener('click', function() {
  console.log('woo')
});

function replaceWith(original, tag) {
  tag = tag.replace(/<|>/g, '');
  var newEl = document.createElement(tag);
  while (original.firstChild) {
    newEl.appendChild(original.firstChild)
  }

  original.parentNode.replaceChild(newEl, original);
}

var elements = document.querySelectorAll('img + span'),
  elementArray = Array.prototype.slice.call(elements, 0);

elementArray.forEach(function(elem) {
  replaceWith(elem, '<figcaption>')
});
span {
  color: limegreen;
}
figcaption {
  color: #f90;
}
<img src="whatever.jpg" />
<span><span>Copyright</span> Stackoverflow</span>

JS Fiddle demo.

References:

David Thomas
  • 249,100
  • 51
  • 377
  • 410
  • This is a very comprehensive answer, but I need to get the finished HTML into a variable because it gets used in different places later on. So all this `
    Copyright
    ` must be inside a var.
    – volume one Dec 02 '15 at 21:07
  • Then you should [edit] your question to show the variable being assigned, and, just to be clear, the resulting variable, and its value. – David Thomas Dec 02 '15 at 22:27
  • Its there in the final line of the code that I needed: `var $contents = $(this).add($figcaption);`. The `$contents` should be the finished HTML ready to insert anywhere on the DOM. – volume one Dec 02 '15 at 23:20
2
$(function() {
  //create figcaption empty
  var figcaption = $('<figcaption/>');

  //get span
  var span = $('span:first');

  //replacement behind the scenes
  figcaption.html(span.html());

  //replace in dom
  span.replaceWith(figcaption);

  //verify
  alert($('body').html());
});

https://jsfiddle.net/rodrigo/Lfvotuze/

Ro.
  • 1,525
  • 1
  • 14
  • 17
1

Try something similar:

$elem.find("img").each(function(innerIndex) {
    var span = $(this).next('span');//gets the next span from the img
    var $contents = span.text();//gets the content
    span.remove();//removes the span
    //adds the figcaption after the img tag
    $(this).after('<figcaption>'+$contents+'</figcaption>');
});

jsfiddle: https://jsfiddle.net/t5dLcw12/

Mark Schultheiss
  • 32,614
  • 12
  • 69
  • 100
madalinivascu
  • 32,064
  • 4
  • 39
  • 55
0

This is pretty simple really. Wrap the span inner with our new tag, then remove the span with unwrap of that new tag then create a clone of both of those (myclone now holds your "new" elements):

$('span').wrapInner('<figcaption/>').find('figcaption').unwrap();
var myclone = $('img').add('figcaption').clone();

Same thing with one chain:

var myclonea = $('span').wrapInner('<figcaption/>').find('figcaption').unwrap().add('img').clone();

NOTE With regard to event handlers on the original wrapper that might need to be moved etc. to the new wrapper, you can reference this post if needed: jQuery find events handlers registered with an object

Community
  • 1
  • 1
Mark Schultheiss
  • 32,614
  • 12
  • 69
  • 100