30 个有用的 JavaScript 代码片段(下)

2023年 10月 11日 60.5k 0

今天这篇文章,接上昨天的《30 个有用的 JavaScript 代码片段(上)》,我们把剩下的18个代码片段分享在这篇文章中。

现在,我们就开始这篇文章的内容吧。

13. 创建一个 DOM 元素

// Code snippet to render  DOM element on the fly
const loadImage = (url) => new Promise((resolve, reject) => {
    const img = new Image();
    img.addEventListener('load', () => resolve(img));
    img.addEventListener('error', (err) => reject(err));
    img.src = url;
});
// __USAGE Example__
var uploadFileInput=document.createElement('input');
uploadFileInput.type='file';
uploadFileInput.id='uploadFileInput';
document.body.appendChild(uploadFileInput);


function readFileAsDataURL(file) {
    return new Promise((resolve,reject) => {
        let fileredr = new FileReader();
        fileredr.onload = () => resolve(fileredr.result);
        fileredr.onerror = () => reject(fileredr);
        fileredr.readAsDataURL(file);
    });
}


uploadFileInput.addEventListener('change', async (evt)=> {
  var file = evt.currentTarget.files[0];
    if(!file) return;


    var b64str = await readFileAsDataURL(file);
    var _img = await loadImage(b64str);
    _img['style']['width']=`${_img.naturalWidth}px`;
    _img['style']['height']=`${_img.naturalHeight}px`;
    _img['style']['margin']='2px auto';
    _img['style']['display']='block';
    document.body.appendChild(_img);
}, false);

注意:虽然大多数 HTML 元素节点的属性(例如

)是可访问的并且不需要“load”事件的侦听器,但 是此规则的一个例外(另一个唯一的例外是 )

因此,如果 document.createElement('img') 没有返回 Promise:

var loadedImg=document.createElement('img');
loadedImg.src=; // dataURL refers to the encoded image data

变量loadedImg不会被渲染,因为在分配属性src时它仍然是未定义的。

14. 删除所有嵌套子节点

const removeAllChildNodes = ((parentEle) => parentEle.children.length > 0  ? parentEle.removeChild(parentEle.children[0]) : null);
// __USAGE Example__
const _scale = window.devicePixelRatio*2;
const size = 250;
const fontSize=size/_scale;


var canvasDisplay=document.createElement('div');
canvasDisplay['style']['width']=`${size+4}px`;
canvasDisplay['style']['height']=`${size+4}px`;
canvasDisplay['style']['margin']='1px auto';
document.body.appendChild(canvasDisplay);


var iconBtn_1=document.createElement('button');
iconBtn_1.type='button';
iconBtn_1.value='🔍';
iconBtn_1.innerText='🔍';
document.body.appendChild(iconBtn_1);


var iconBtn_2=document.createElement('button');
iconBtn_2.type='button';
iconBtn_2.value='💡';
iconBtn_2.innerText='💡';
document.body.appendChild(iconBtn_2);


function setIcon(icon) {
  var canvas=document.createElement('canvas');
  canvas.width=size;
  canvas.height=size;
  canvas['style']['margin']='1px auto';
  canvas['style']['width']=`${fontSize}px`;
  canvas['style']['height']=`${fontSize}px`;
  canvas['style']['border']='1px dotted #6c757d';
  canvas['style']['background']='transparent';


  var ctx=canvas.getContext('2d');
  ctx.scale(_scale,_scale);
  ctx.font = `${fontSize}px Segoe Ui Emoji`;
  ctx.textAlign = 'center';
  ctx.textBaseline='middle';
  ctx.globalAlpha=1.0;
  ctx.fontVariantCaps='unicase';
  ctx.filter='none';
  ctx.globalCompositeOperation='source-over';
  ctx.imageSmoothingEnabled=true;
  ctx.imageSmoothingQuality='high';
  ctx.letterSpacing='0px';
  ctx.lineWidth=0;
  ctx.shadowColor='rgba(0, 0, 0, 0)';
  ctx.strokeStyle='#000000';
  ctx.fillStyle='#ffffff';


  const _x=((canvas.width/_scale)/2);
  const _y=((canvas.height/_scale)/2);
  ctx.fillText(icon, _x, _y);


  removeAllChildNodes(canvasDisplay);
  canvasDisplay.appendChild(canvas);
}


iconBtn_1.addEventListener('click', ()=> {
  setIcon(iconBtn_1.value);
});
iconBtn_2.addEventListener('click', ()=> {
  setIcon(iconBtn_2.value);
});

使用示例:

注意:函数removeAllChildNodes()使用递归来删除所有嵌套元素。这对于防止先前不需要的子节点与后续附加的子节点累积是必要的,如下所示:

15. 选择文本并将其复制到剪贴板 — 表单输入和其他 HTML DOM 元素

function selectCopyText(nodeId) {
    let isVal=true;
    let node = document.getElementById(nodeId);
    try {
        node.select();
        try { node.setSelectionRange(0, 99999);} catch(err0) {}
    } catch(err) {
        isVal=false;
        if (document.body.createTextRange) {
            const range = document.body.createTextRange();
            range.moveToElementText(node);
            range.select();
        } else if (window.getSelection) {
            const selection = window.getSelection();
            const range = document.createRange();
            range.selectNodeContents(node);
            selection.removeAllRanges();
            selection.addRange(range);
        } else { console.warn("Could not select text in node: Unsupported browser."); }
    } finally { navigator.clipboard.writeText(isVal ? node.value : node.innerText);}
}
const strToCopy = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Viverra accumsan in nisl nisi scelerisque eu ultrices. Posuere lorem ipsum dolor sit amet consectetur adipiscing. Sodales ut etiam sit amet nisl purus.';


var _copyBtn=document.createElement('button');
_copyBtn.type='button';
_copyBtn.value='toCopy';
_copyBtn.innerText='Copy Textarea';


var _textarea=document.createElement('textarea');
_textarea.id='toCopy';
_textarea['style']['resize']='none';
_textarea['style']['width']='100%';
_textarea['style']['height']='100px';
_textarea['style']['display']='block';
_textarea.value=strToCopy;
document.body.appendChild(_copyBtn);
document.body.appendChild(_textarea);


var _copyBtn2=document.createElement('button');
_copyBtn2.type='button';
_copyBtn2.value='toCopy2';
_copyBtn2.innerText='Copy DIV';


var _div=document.createElement('_div');
_div.id='toCopy2';
_div['style']['width']='100%';
_div['style']['height']='100px';
_div['style']['display']='block';
_div['style']['border']='1px dashed #000000';
_div.innerText=strToCopy;
document.body.appendChild(_copyBtn2);
document.body.appendChild(_div);


_copyBtn.addEventListener('click', ()=> {
  selectCopyText(_copyBtn.value);
});
_copyBtn2.addEventListener('click', ()=> {
  selectCopyText(_copyBtn2.value);
});

使用示例:

虽然“复制到剪贴板”是最普遍需要的 JavaScript 功能之一,但还值得注意的是,所需的文本内容同样可能包含在表单元素中,例如 - 或嵌入 HTML DOM 元素中,例如

因此,此函数解决了通过检查 HTMLInputElement 或 HTMLDivElement 中是否存在文本内容来确定如何在 JavaScript 中实现文本选择的问题。

然后分别选择属性.value 和.innerText。

16. 将 JSON 对象数组转换为 CSV 文本

function rowJSONToCSV(rowJSONObj) {
    let csvOutputStr='';
    let allHeaders={};
    for(let obj of rowJSONObj) {
      for(let k in obj) { allHeaders[k]=''; }
    }
    let allHeadersArr=Object.keys(allHeaders);
    let headerStr=JSON.stringify(allHeadersArr);
    headerStr=headerStr.substring(1,headerStr.length-1);
    csvOutputStr+=headerStr + 'n';


    for(let obj of rowJSONObj) {
      let allValuesArr=[];
      for(let headerField of allHeadersArr) {
        let rowVal=obj[headerField];
        (typeof rowVal !== 'undefined') ? allValuesArr.push(rowVal) : allValuesArr.push('NULL')
      }
      let rowStr=JSON.stringify(allValuesArr);
      rowStr=rowStr.substring(1,rowStr.length-1);
      csvOutputStr+=rowStr + 'n';
    }
    return Promise.resolve(csvOutputStr);
}
var uploadFileInput=document.createElement('input');
uploadFileInput.type='file';
uploadFileInput.id='uploadFileInput';
document.body.appendChild(uploadFileInput);


function readFileAsText(file) {
    return new Promise((resolve,reject) => {
        let fileredr = new FileReader();
        fileredr.onload = () => resolve(fileredr.result);
        fileredr.onerror = () => reject(fileredr);
        fileredr.readAsText(file);
    });
}
uploadFileInput.addEventListener('change', async (evt)=> {
    var file = evt.currentTarget.files[0];
    if(!file) return;


    var jsonStr = await readFileAsText(file);
    var inputJSONObj = JSON.parse(jsonStr);
    var divOne = document.createElement('div');
    divOne['style']['font-family']='Consolas';
    divOne['style']['height']='250px';
    divOne['style']['weight']='100%';
    divOne['style']['margin']='2px auto';
    divOne['style']['overflow']='auto';
    divOne['style']['border']='1px dashed #17a2b8';
    var csvStrOutput=await rowJSONToCSV(inputJSONObj);
    divOne.innerText=csvStrOutput;
    document.body.appendChild(divOne);
}, false);

使用示例

演示示例数据文件的链接:icd10_data.json

输出 CSV 文件的链接:icd10_data.csv

基本原理

按照惯例,我倾向于使用插件 json2csv.js(最初来自 https://www.npmjs.com/package/json2csv)来进行 JSON 到 CSV 的转换。

然而,对于超过 50MB 的大文件,递归算法超出了其最大堆栈限制,当我尝试将文件上传到我开发的 Web 实用程序时,我发现了这一点:https://tableau-data-utility。onrender.com/ 在 [🌐 Spatial⇢CSV] 选项卡上

由于地理空间数据集往往超过 50MB,实现上述将 JSON 转换为 CSV 的函数解决了递归问题。

局限性

仅适用于深度级别为 1 的嵌套 JSON 对象。如果输入深度级别更高的 JSON 对象,则效果不佳。例如,以下内容将失败,因为第一个条目“喜欢”的深度级别 = 2

[
   { 
    "firstName":"John", 
    "lastName":"Doe",
    "likes": {
     "pets": ["dogs","cats"]
    }
   },
   { 
    "firstName":"Anna", 
    "lastName":"Smith" 
   }
]

17.DOMContentLoaded 事件监听器

if (document.readyState === 'complete' || document.readyState !== 'loading' && !document.documentElement.doScroll) {
  callback();
} else {
  document.addEventListener('DOMContentLoaded', async() => {
    /* TO DO LOGIC HERE */
  });
}

    
    
    Demo (5)  
      
      

🔄 Refresh webpage to run function "doSomething()".

if (document.readyState === 'complete' || document.readyState !== 'loading' && !document.documentElement.doScroll) { callback(); } else { document.addEventListener('DOMContentLoaded', async() => { function doSomething() { alert('Hello!'); } doSomething(); }); }

    
    
     
      Demo (5)
        
         function doSomething() {  
            alert('Hello!');  
         }  
  
      
      
      

🔄 Refresh webpage to run function "doSomething()".

使用示例

当网页加载后需要立即执行某个功能时,一种可能的方法是将 onload 事件嵌入到 标记中:

注意:一个常见的错误是执行 windows.onload 中嵌套的函数:


    
    
     
      Demo (5)
      
        windows.onload = function() {
          function doSomething() {  
              alert('Hello!');  
             }
        };
 
      
      
      

🔄 Refresh webpage to run function "doSomething()".

因此,为了对所有嵌入的 JavaScript 代码片段进行稍微更有条理的分区,请使用以下命令:

document.addEventListener('DOMContentLoaded', function() { ... });

可以考虑改为:

18. 将多个事件绑定到 HTML 元素

// Binding multiple events to a single element
function addMultipleEvents(eventsArray, targetElem, handler) {
  eventsArray.map((event) => targetElem.on(event, handler));
}


// Alt Version: Using 'addEventListener'
function addMultipleEvents(eventsArray, targetElem, handler) {
  eventsArray.map((event) => targetElem.addEventListener(event, handler));
}
// Noting the coordinates when map is pan or zoom etc.
// Built with Leaflet.js
var mapUrl="https://stamen-tiles.a.ssl.fastly.net/toner-hybrid/{z}/{x}/{y}.png";


var map = L.map("map");
var basemap=L.tileLayer(mapUrl, {
    detectRetina: true,
    maxZoom: 19,
    minZoom: 11,
    attributionControl: false
}).addTo(map);
function renderImageBounds() {
  imgBounds=map.getBounds();
  imgBounds_Left.innerHTML=imgBounds._southWest.lng;
  imgBounds_Right.innerHTML=imgBounds._northEast.lng;


  imgBounds_Bottom.innerHTML=imgBounds._southWest.lat;
  imgBounds_Top.innerHTML=imgBounds._northEast.lat;
}
function addMultipleEvents(eventsArray, targetElem, handler) {
  eventsArray.map((event) => targetElem.on(event, handler));
}
addMultipleEvents(['zoomend', 'dragend', 'viewreset', 'moveend', 'load', 'resize'], map, renderImageBounds);

使用示例

在我之前构建的网络实用程序中,合并了跟踪所显示地图的地理坐标的功能,如下所示:

19.清除浏览器缓存

清除单个网页浏览器缓存的 JavaScript 如下:

const supportsLocalStorage = () => {
    if (!('localStorage' in window)) return false;
    try {
        localStorage.setItem('check', 'true');
        localStorage.getItem('check');
        localStorage.removeItem('check');
        return true;
    } catch (e) {
        return false;
    }
};
const hasLocalStorage = supportsLocalStorage();
/* Assume there is a  */
/* HTML element in the DOM */
/*const clearCacheBtn = document.getElementById('clearCache');*/
/*clearCacheBtn.addEventListener('click', async()=> {*/
    requestAnimationFrame(() => {
        if (hasLocalStorage) {
            localStorage.clear();
            location.reload();
        }
    });
/*});*/

用例演示

在某些网页上,以前的用户输入会有意保留在浏览器缓存中。 例如,在 JSONEditor.com 上:

但是,当通过 JavaScript 控制台以编程方式清除浏览器缓存时:

20. 以编程方式触发 HTML 事件

// Note: Compatible for both IE8, IE9+ and other modern browsers
function triggerEvent(el, type) {
    let e = ( ('createEvent' in document) ? document.createEvent('HTMLEvents') : document.createEventObject() );
    if ('createEvent' in document) { 
      e.initEvent(type, false, true);
      el.dispatchEvent(e);
    } else { 
      e.eventType = type;
      el.fireEvent('on' + e.eventType, e);
    }
}

用例演示

假设以下 HTML 标记文档:



  Demo trigger() Method
  
      0
      Increase #1
      0
      Increase #2
      
        /* TO-DO */

  

上面的triggerEvent()函数可以在上面的2个标签之间实现,如下所示:

function triggerEvent(el, type) {
    let e = ( ('createEvent' in document) ? document.createEvent('HTMLEvents') : document.createEventObject() );
    if ('createEvent' in document) { 
      e.initEvent(type, false, true);
      el.dispatchEvent(e);
    } else { 
      e.eventType = type;
      el.fireEvent('on' + e.eventType, e);
    }
}
document.querySelector('#btn1').addEventListener('click', () => {
  increase(document.querySelector('.box-1>h1'));
});
document.querySelector('#btn2').addEventListener('click', () => {
  triggerEvent(document.querySelector('#btn1'), 'click');
  increase(document.querySelector('.box-2>h1'));
});
function increase(obj) {
    let text = parseInt(obj.textContent, 10);
    obj.textContent = text + 1;
}

虽然, jQuery 插件有包装函数 trigger() ,它输出相同的结果。


// Sample jQuery code snippet at /* TO-DO */ between the 2  tags:
$('#btn1').click(() => {
  increase($('.box-1>h1'));
});
$('#btn2').click(() => {
  $('#btn1').trigger('click');
  increase($('.box-2>h1'));
});
function increase(obj) {
  let text = parseInt(obj.text(), 10);
  obj.text(text + 1);
}

在首选更轻量级替代方案的情况下,可以考虑使用 functiontriggerEvent()。

21. 将 async-await 与 for 循环结合使用

下面的代码片段调用 JSON API,其中每次迭代都会解析不同的参数 (i),以按顺序检索 JSON 响应。

(async()=> {
    // Assume a 
is in the HTML DOM const datatableTbody = document.querySelector('#datatable tbody'); for (let i=1;i { const datatableTbody = document.querySelector('#datatable tbody'); for (let i=1;i ( (str.toLowerCase()).replace(/w+/g, ((str) => ( str.charAt(0).toUpperCase()+str.substr(1) ).replace(/r/g, '')) ) ); // Usage: // let str = 'HeLLO world'; // console.log(toPascalCase(str)); // Result: 'Hello World'

用例演示

当从 API 返回的字母大小写不一致时,为了视觉一致性,以帕斯卡大小写显示某些标签可能更合适。 

例如,在我的一个业余项目中——一个 Bus ETA Web 应用程序,检索所有公交车站描述的 API 的字母大小写不一致:

23. 将 Uint8Array 转换为 Base64 字符串

除了纯文本之外,JavaScript 还读取其他格式的文件输入,例如 ArrayBuffers(尤其是多媒体上传)。 从 ArrayBuffer 检索文件数据作为 Base64 编码字符串 (DataURL) 的函数如下:

// Uint8Array to Base64 Encoded String
const convertBitArrtoB64 = (bitArr) => ( btoa( bitArr.reduce((data, byte) => data + String.fromCharCode(byte), '') ) );

用例演示

对于某些库,所有处理的文件输入仅输出为 Uint8Array。 一个例子是 FFmpeg.wasm(归功于吴杰罗姆),我在一个网络应用程序中实现了它,用于对音频编解码器进行转码。 在这个副项目中的用法如下:

// `data` is output as Uint8Array 
// assuming input format is mp3, `outputFileExt`='mp3'
const data = ffmpeg.FS('readFile', `output${outputFileExt}`);
let b64Str = convertBitArrtoB64(data);


// assuming converted output format is mp4, `outputFileMimeType` = 'mp4'
let encodedData=`data:${outputFileMimeType};base64,${b64Str}`;


// To generate download link and save output
let dwnlnk = document.createElement('a');
dwnlnk.download = `${saveFilename}${outputFileExt}`;
dwnlnk.href = encodedData;
dwnlnk.click();

24.将Base64字符串转换为Uint8Array

反之,Uint8Array→DataURL转换的逆过程如下:

// Base64 Encoded String to Uint8Array
const convertB64ToBitArr = (b64Str) => ( Uint8Array.from(atob( (b64Str.includes(';base64,') ? (b64Str.split(','))[1] : b64Str) ), (v) => v.charCodeAt(0)) );

25.获取浏览器垂直滚动条宽度

function getScrollbarWidth() {
    const docEle=document.documentElement;
    const cssOverflowY=docEle.style.overflowY; // stores prev value
    docEle.style.overflowY='scroll'; // force scrollbar to appear
    const scrollbarWidth = window.innerWidth - docEle.clientWidth;
    docEle.style.overflowY=cssOverflowY; // reset to prev value


    return scrollbarWidth;
}
const sbWidth=getScrollbarWidth(); // 17


/* comments */
// In which case, scrollbar width is 17px for my browser 
// and width of elements should factor in this value in its css attributes

26. 将年、月和日添加到 Date() 对象

虽然 JavaScript 没有特定于 Date() 对象的时间单位减法/加法的内置方法,但可以使用辅助方法来构造以下实用函数。

function addYears(date, n) {
    const dateCopy = new Date(date);
    dateCopy.setFullYear(dateCopy.getFullYear() + n);
    return dateCopy;
}
function addMonths(date, n) {
    const dateCopy = new Date(date);
    dateCopy.setMonth(dateCopy.getMonth() + n);
    return dateCopy;
}
function addDays(date, n) {
    const dateCopy = new Date(date);
    dateCopy.setDate(dateCopy.getDate() + n);
    return dateCopy;
}

使用示例:

const currentDate=new Date();
console.log(`Current date: ${currentDate}`);
console.log(`Current date + 11 years: ${addYears(currentDate, 11)}`);
console.log(`Current date + 11 months: ${addMonths(currentDate, 11)}`);
console.log(`Current date + 11 days: ${addDays(currentDate, 11)}`);


/* Sample output */
// Current date: Fri Sep 22 2023 23:52:57 GMT+0800 (Singapore Standard Time)
// Current date + 11 years: Fri Sep 22 2034 23:52:57 GMT+0800 (Singapore Standard Time)
// Current date + 11 months: Thu Aug 22 2024 23:52:57 GMT+0800 (Singapore Standard Time)
// Current date + 11 days: Tue Oct 03 2023 23:52:57 GMT+0800 (Singapore Standard Time)

27.从数组中删除无效值,例如null和undefined

const removeInvalidVals = ((arr) => arr.filter(ele => (ele !== null && typeof ele !== 'undefined') ? true : false));

上面的函数从数组中删除空值和未定义的值。 一个常见的用例是从 API 调用的 JSON 响应中排除无效值。

使用示例:

let arr = [1, 3, undefined, 5, null, null, 8];
arr = removeInvalidVals(arr);
console.log(arr);


/* Expected output */
// [1, 3, 5, 8]

28. 数组中的唯一值

操作数组的另一个方便的实用函数是删除其中的重复值 。

const uniqueArr = ((arr) => (Array.from(new Set(arr))));

使用示例:

let arr = [1, 1, 3, 2, 5, 3, 4, 7, 7, 7, 8];
arr = uniqueArr(arr);
console.log(arr);


/* Expected output */
// [1, 3, 2, 5, 4, 7, 8]

29. 通过解析字符串创建新的 HTML DOM 元素

HTML 标签用于保存 HTML 内容以供稍后呈现。 下面的函数 htmlToElement() 不是使用 createElement() 在 JavaScript 中从头开始创建元素,而是直接从 HTML 字符串创建子节点。

function htmlToElement(html) {
    if (!(document.createElement("template").content)) {
        alert('Your browser does not support "template".');
        return;
    }
    let documentFragment = document.createDocumentFragment();
    let template = document.createElement('template');
    template.innerHTML = html.trim();
    for (let i = 0, e = template.content.childNodes.length; i < e; i++) {
        documentFragment.appendChild(template.content.childNodes[i].cloneNode(true));
    }
    return documentFragment;
}

使用示例



  
  
    
  


  
  
    
  

30. 打印网页

/* Assume there is a  HTML element in the DOM */
// Print
const printPageBtn = document.getElementById('printPage');
printPageBtn.addEventListener('click', ()=> {
    requestAnimationFrame(() => {
        window.print();
    });
});

演示:

总结

到这里,我们的30个有用的JavaScript的代码片段就分享完了,希望这些代码能够帮助到你。

相关文章

JavaScript2024新功能:Object.groupBy、正则表达式v标志
PHP trim 函数对多字节字符的使用和限制
新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
为React 19做准备:WordPress 6.6用户指南
如何删除WordPress中的所有评论

发布评论