Home > Back-end >  show image in table html
show image in table html

Time:09-26

I need help on this. Check if the image_file_name exists in html table first then show_image in another cell using javascript.I provided 2 images in down blow. First image contains a image folder where I have a list of images and the second image shows the layout of HTML page

[image floder][1]: https://i.stack.imgur.com/nbbgH.png

[HTML page showing the table][2]: https://i.stack.imgur.com/cgQ5N.png

  <table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th>Image_id</th>
      <th class="image_file_name">image_file_name</th>
      <th>Image_Text_output </th>
      <th>date</th>
      <th id="show_image">Show_image</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1</td>
      <td>./imagetocsv/img/MessageMediaPhoto-ID-158.jpg</td>
      <td>test 2</td>
      <td>24-09-2021 12:20:47</td>
      <td>NaN</td>
    </tr>
    <tr>
      <td>2</td>
      <td>./imagetocsv/img/MessageMediaPhoto-ID-428.jpg</td>
      <td>test 2</td>
      <td>24-09-2021 12:20:47</td>
      <td>NaN</td>
    </tr>

CodePudding user response:

Alright, let's break this problem down to smaller ones, so it's easier to tackle it.

We need to:

  • Find a column element based on its class/id (.image_file_name and #show_image)
  • Find the index of a column. So we know which data cell (<td>) to get based on the index of a header cell (<th>)
  • Check if an image exists (whether it returns 200 or 404 when we attempt to fetch it)
  • Create image elements and append them to the appropriate row/column

Let's create a few helper functions for that:

/**
 * Return the index of the given column.
 *
 * @param {HTMLElement} column
 * @return {number}
 */
 const getColumnIndex = (column) => {
  const row = column.parentNode;
  const columns = [...row.children];
  return columns.indexOf(column);
}

The getColumnIndex() function accepts a column (as an HTMLElement) and returns its index.

/**
 * Asynchonously check if the file with the given filename exists.
 *
 * @return {Promise<boolean>}
 */
const fileExists = async (filename) => {
  const response = await fetch(filename, { method: 'HEAD' });
  return response.ok;
}

The fileExists() function is asynchronous (returns a Promise). It accepts the image filename and returns a Promise which resolves to a boolean, indicating whether the image exists (responded with a 200 OK status) or not (responded with a non-ok status, i.e. 404 Not Found).

/**
 * Create an image element with the given URL.
 *
 * @param {string} url
 * @return {HTMLElement}
 */
const createImageElement = (url) => {
  const image = document.createElement('img');
  image.setAttribute('src', url);
  return image;
};

The createImageElement() function accepts a URL, creates an <img> element and returns it.

Now that we have our helper functions, let's write the rest of it. We'll listen for the DOMContentLoaded event to make sure the initial HTML document has been completely loaded (i.e. our <table> exists).

/**
 * Run the initial HTML document has been completely loaded.
 */
const init = async () => {
  // Do stuff
}

window.addEventListener('DOMContentLoaded', init);

Let's get the table and its rows:

// Get the table and its rows
const table = document.querySelector('.dataframe');
const rows = table.querySelectorAll('tbody > tr');

If we know our table's layout we can explicitly set the image_file_name and show_image indices:

const filenameIndex = 1;
const imageIndex = 4;

Otherwise, we can utilize our getColumnIndex() function to find them, like this:

// Get the `image_file_name` and `show_image` columns
const filenameColumn = table.querySelector('th.image_file_name');
const imageColumn = table.querySelector('th#show_image');

const filenameIndex = getColumnIndex(filenameColumn);
const imageIndex = getColumnIndex(imageColumn);

Finally, we're going to iterate over each row, get the filename, check if that image exists, and display it if it does.

// Iterate over each row
for await (const row of rows) {
  // Get all columns of the currently iterated row
  const columns = row.children;

  // Get the filename
  const filename = columns[filenameIndex].textContent;

  // Check if the image exists
  const imageExists = await fileExists(filename);

  // If it exists
  if (imageExists) {
    // Clear the `show_image` column
    columns[imageIndex].innerHTML = '';

    // Create an image element
    const image = createImageElement(filename);

    // Add the image
    columns[imageIndex].appendChild(image);
  }
}

Putting it all together:

<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th>Image_id</th>
      <th class="image_file_name">image_file_name</th>
      <th>Image_Text_output </th>
      <th>date</th>
      <th id="show_image">Show_image</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1</td>
      <td>./imagetocsv/img/MessageMediaPhoto-ID-158.jpg</td>
      <td>test 2</td>
      <td>24-09-2021 12:20:47</td>
      <td>NaN</td>
    </tr>
    <tr>
      <td>2</td>
      <td>./imagetocsv/img/MessageMediaPhoto-ID-428.jpg</td>
      <td>test 2</td>
      <td>24-09-2021 12:20:47</td>
      <td>NaN</td>
    </tr>
  </tbody>
</table>
<script type="text/javascript">
  /**
    * Return the index of the given column.
    *
    * @param {HTMLElement} column
    * @return {number}
    */
  const getColumnIndex = (column) => {
    const row = column.parentNode;
    const columns = [...row.children];
    return columns.indexOf(column);
  }

  /**
    * Asynchonously check if the file with the given filename exists.
    *
    * @return {Promise<boolean>}
    */
  const fileExists = async (filename) => {
    const response = await fetch(filename, { method: 'HEAD' });
    return response.ok;
  }

  /**
    * Create an image element with the given URL.
    *
    * @param {string} url
    * @return {HTMLElement}
    */
  const createImageElement = (url) => {
    const image = document.createElement('img');
    image.setAttribute('src', url);
    return image;
  };

  /**
    * Run  the initial HTML document has been completely loaded.
    */
  const init = async () => {
    // Get the table element
    const table = document.querySelector('.dataframe');

    // Get the `image_file_name` and `show_image` columns
    const filenameColumn = table.querySelector('th.image_file_name');
    const imageColumn = table.querySelector('th#show_image');
    
    const filenameIndex = getColumnIndex(filenameColumn);
    const imageIndex = getColumnIndex(imageColumn);

    // Get the table rows
    const rows = table.querySelectorAll('tbody > tr');

    // Iterate over each row
    for await (const row of rows) {
      // Get all columns of the currently iterated row
      const columns = row.children;

      // Get the filename
      const filename = columns[filenameIndex].textContent;

      // Check if the image exists
      const imageExists = await fileExists(filename);

      // If it exists
      if (imageExists) {
        // Clear the `show_image` column
        columns[imageIndex].innerHTML = '';

        // Create an image element
        const image = createImageElement(filename);

        // Add the image
        columns[imageIndex].appendChild(image);
      }
    }
  };

  window.addEventListener('DOMContentLoaded', init);
</script>

Disclaimer: This answer includes arrow functions, promises, async/await, the spread operator, and other ES6 features.

CodePudding user response:

My approach would be to create an image object in each table row, then set the source to the content of the second column. To do that we need to give the tbody element an id, then iterate trought every child of it.

Like this:

function displayPictures(){
    //Get the tbody element
    var tbody=document.getElementById("tbody")
    //For each row
    for (var row of tbody){
        //We create an image object 
        var imageObject=new Image()
        //We set the image source to the 2nd columns content
        imageObject.src=row[1].innerText
        //And finally, we replace the 5th cell's content
        row[4].innerHTML=""
        row[4].appendChild(imageObject)
    }
}

To make this a bit more memory efficient, we can declare each local variable in a single var statement, like this:

function displayPictures(){
    //Deaclaring variables
    var tbody,row,imageObject
    //Get the tbody element
    tbody=document.getElementById("tbody")
    //For each row
    for (row of tbody){
        //We create an image object 
        imageObject=new Image()
        //We set the image source to the 2nd columns content
        imageObject.src=row[1].innerText
        //And finally, we replace the 5th cell's content
        row[4].innerHTML=""
        row[4].appendChild(imageObject)
    }
}

You can easily manipulate the pictures (eg:setting their height and width) right before inserting them.


The other method would be to fetch the pictures, but it's not necessary, because Cross Origin Resource Sharing policy does not affect simple image queries (as far as I know).

Edit: Over-engineer's Ansver reminded me of the error handling: You can simply attach an onerror handler to the Image object, to set the placeholder image or more easily just set an alt text which displays when the image fails to show up.

//This sets a placeholder image if any error occours.
imageObject.onerror=function(e){this.src=[path to placeholder image]}
//The alt text is only shown when the image fails to load properly.
//It's mostly used to describe the functionality of the image, but in this case, the 'Not found' is enough.
imageObject.alt="Not found"

This method does not rely purely on JS, it uses the HTML's functionality too.

  • Related